API de validación documental

Envía un documento (PDF, Word, imágenes…) junto con el tipo de documento esperado y recibe si es válido. Es un filtro previo automático: la revisión final la hace una persona.

URL basehttps://app-qqqarmamumfa68y4hmgskosj.launchlane.space/api/v1

Todas las respuestas son JSON. Envía siempre la cabecera Accept: application/json.

Autenticación y permisos

Cada sistema cliente recibe una o varias claves de API. Envíala en cada petición:

Authorization: Bearer 12|AbCdEf...

Cada clave tiene permisos concretos:

PermisoPermite
documents:validatePOST /validations
validations:readGET /validations/{id}
document-types:readGET /document-types

Un cliente puede tener restringidos los tipos de documento que puede usar. Las claves pueden tener fecha de caducidad y revocarse en cualquier momento.

Tipos de documento

GET/document-types

Lista los tipos de documento activos que tu cliente puede usar, con los parámetros obligatorios de cada uno.

{
  "data": [
    {
      "code": "acreditacion_vinculacion_entidad",
      "name": "Acreditación de la vinculación con la Entidad",
      "description": "Autorización firmada por un apoderado...\n\n- La entidad que autoriza es **{{nombre_entidad}}** (CIF {{cif_entidad}}).",
      "version": 3,
      "parameters": ["nombre_entidad", "cif_entidad"],
      "optional_parameters": []
    }
  ]
}

Validar un documento

POST/validationsmultipart/form-data

Campos

CampoTipoDescripción
file *ficheroEl documento. Usa file o files[].
files[] *ficherosVarios ficheros que forman un único documento (por ejemplo, una foto de cada página). Máximo 30.
document_type *textoCódigo del tipo de documento esperado.
parametersobjetoValores de los marcadores de la especificación. Como JSON (parameters={"cif_entidad":"G12345678"}) o como campos (parameters[cif_entidad]=G12345678). Obligatorio si el tipo tiene parámetros.
external_referencetextoTu identificador (expediente, solicitud…). Se devuelve en la respuesta. Máx. 191 caracteres.
callback_urlURLSi se indica, la petición es asíncrona y el resultado se envía a esta URL. Debe ser https y pública.

* obligatorio.

Parámetros y marcadores

La especificación de cada tipo de documento puede incluir marcadores con la forma {{nombre}}. Antes de analizar el documento se sustituyen por los valores que envíes en parameters:

Especificación:   El CIF del solicitante tiene que ser {{cif_solicitante}}
parameters:       {"cif_solicitante": "12345678B"}
Resultado:        El CIF del solicitante tiene que ser 12345678B

Consulta los parámetros de cada tipo en GET /document-types: parameters son obligatorios (si falta alguno, la API responde 422) y optional_parameters son opcionales (si no los envías, ese dato no se comprueba). Los parámetros que sobren se ignoran. Los valores no se guardan.

Formatos y conversión

FormatoTratamiento
PDFLas firmas electrónicas incrustadas se detectan en el fichero original y se le indican a la IA (no se verifican). Si es grande, se comprime reduciendo la resolución de las imágenes. Si el modelo de IA no lee PDF, cada página se convierte a imagen (máximo 30 páginas; si hay más, se indica en processing_notes).
JPG, PNG, GIF, WEBPSe endereza según la orientación de la foto y se reduce si es muy grande.
HEIC/HEIF, TIFF (multipágina), BMP, AVIF…Se convierte a JPEG.
DOC, DOCX, ODT, RTF, XLS, XLSX, ODS, PPT, PPTX, TXT…Se convierte a PDF.
OtrosSe intenta convertir. Si no es posible, la solicitud termina con status: failed y el motivo en error.

Límites: 50 MB por fichero y 22 MB para el documento completo tras la conversión. Las conversiones aplicadas se indican en processing_notes.

Modo síncrono

Si no envías callback_url, la respuesta llega cuando termina el análisis (normalmente entre 10 y 60 segundos; configura un timeout de al menos 5 minutos). Aun así, cada solicitud recibe un id único que puedes consultar después.

curl -X POST https://app-qqqarmamumfa68y4hmgskosj.launchlane.space/api/v1/validations \
  -H "Authorization: Bearer $API_KEY" \
  -H "Accept: application/json" \
  -F "document_type=acreditacion_vinculacion_entidad" \
  -F "files[]=@pagina1.jpg" \
  -F "files[]=@pagina2.jpg" \
  -F 'parameters={"nombre_entidad":"Fundación Ejemplo","cif_entidad":"G12345678"}' \
  -F "external_reference=EXP-2026-0042"

Respuesta 200 OK:

{
  "data": {
    "id": "019a2b3c-4d5e-7f60-8a9b-0c1d2e3f4a5b",
    "status": "completed",
    "mode": "sync",
    "is_valid": true,
    "verdict": "valid",
    "confidence": 0.92,
    "document_type": { "code": "acreditacion_vinculacion_entidad", "name": "Acreditación de la vinculación con la Entidad", "version": 3 },
    "external_reference": "EXP-2026-0042",
    "files": { "count": 2, "total_size_bytes": 3481233, "mime_types": ["image/jpeg"] },
    "processing_notes": [],
    "result": {
      "is_expected_type": true,
      "detected_document_type": "Autorización de representación",
      "summary": "Autorización firmada por el representante legal de Fundación Ejemplo.",
      "requirements": [
        { "requirement": "La entidad que autoriza es Fundación Ejemplo", "status": "met", "evidence": "Encabezado: 'Fundación Ejemplo, con CIF G12345678...'" },
        { "requirement": "Debe estar firmada", "status": "met", "evidence": "Firma manuscrita al pie de la página 2" }
      ],
      "issues": [],
      "legibility": "good"
    },
    "error": null,
    "usage": { "provider": "Proveedor IA", "model": "claude-opus-5-5", "input_tokens": 3512, "output_tokens": 640, "cost": 0.026848, "currency": "EUR", "duration_ms": 18234 },
    "callback": null,
    "created_at": "2026-10-26T10:15:02+00:00",
    "completed_at": "2026-10-26T10:15:20+00:00"
  }
}

Modo asíncrono (callback)

Si envías callback_url, la API responde al momento con 202 Accepted y el identificador de la solicitud:

{
  "data": {
    "id": "019a2b3c-4d5e-7f60-8a9b-0c1d2e3f4a5b",
    "status": "pending",
    "mode": "async",
    "is_valid": null,
    "result": null,
    "callback": { "url": "https://tu-sistema.example/webhooks/validaciones", "status": "pending", "attempts": 0, "delivered_at": null },
    ...
  }
}

Cuando termina, hacemos un POST a tu URL con el mismo JSON que la respuesta síncrona (incluido result, o error si falló) y estas cabeceras:

CabeceraValor
Content-Typeapplication/json
X-Eventvalidation.completed
X-Validation-IdEl id de la solicitud
X-Signaturet=<timestamp>,v1=<firma>

Tu endpoint debe responder con un código 2xx en menos de 15 segundos. Procesa el contenido después de responder si necesitas más tiempo.

Verificar la firma

Cada cliente tiene un secreto de callbacks (whsec_…) que te facilitará el administrador. La firma es un HMAC-SHA256 en hexadecimal de <timestamp>.<cuerpo sin modificar>. Comprueba también que el timestamp no tenga más de 5 minutos.

PHP

$body = file_get_contents('php://input');
preg_match('/^t=(\d+),v1=([a-f0-9]{64})$/', $_SERVER['HTTP_X_SIGNATURE'] ?? '', $m);
$expected = hash_hmac('sha256', $m[1] . '.' . $body, $secret);
$valid = $m && abs(time() - (int) $m[1]) <= 300 && hash_equals($expected, $m[2]);

Node.js

const crypto = require('crypto');
const [, t, v1] = req.get('X-Signature').match(/^t=(\d+),v1=([a-f0-9]{64})$/) || [];
const expected = crypto.createHmac('sha256', secret).update(`${t}.${rawBody}`).digest('hex');
const valid = t && Math.abs(Date.now() / 1000 - Number(t)) <= 300
  && crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(v1));

Python

import hmac, hashlib, re, time
m = re.match(r'^t=(\d+),v1=([a-f0-9]{64})$', request.headers.get('X-Signature', ''))
expected = hmac.new(secret.encode(), f"{m[1]}.".encode() + raw_body, hashlib.sha256).hexdigest()
valid = m and abs(time.time() - int(m[1])) <= 300 and hmac.compare_digest(expected, m[2])
Calcula la firma sobre el cuerpo tal como llega, antes de decodificar el JSON.

Reintentos

Si tu endpoint no responde 2xx o no está disponible, reintentamos 9 veces más, esperando antes de cada intento: 10 s, 1 min, 5 min, 15 min, 30 min, 1,0 h, 2,0 h, 4,0 h, 8,0 h. En total cubrimos caídas de unas 15,9 h. Usa X-Validation-Id para descartar duplicados. El estado de la entrega aparece en callback.status (pending, delivered, failed).

Si agotamos los reintentos, la solicitud no se pierde: consulta GET /validations/{id} para obtener su estado y veredicto en cualquier momento. Te recomendamos tener en tu sistema una opción para consultar las solicitudes que no hayan recibido callback.

Consultar una solicitud

GET/validations/{id}

Devuelve el estado de una solicitud de tu cliente: status, verdict, confidence, consumo y estado del callback. También incluye result con el detalle.

Objeto de respuesta

CampoDescripción
idIdentificador único de la solicitud.
statuspending · processing · completed · failed
modesync o async
is_valid / verdicttrue/valid si el documento es del tipo esperado y contiene los datos esperados; false/invalid en caso contrario. null mientras no ha terminado o si falló.
confidenceSeguridad del veredicto, de 0 a 1.
document_typeTipo esperado y versión de la especificación usada.
filesNúmero de ficheros, tamaño total y formatos detectados.
processing_notesConversiones aplicadas (compresión, conversión a PDF o JPEG…).
result.is_expected_typeSi el documento es del tipo esperado.
result.detected_document_typeQué documento parece ser realmente.
result.summaryMotivo breve del veredicto.
result.requirements[]Cada requisito comprobado: requirement, status (met, not_met, unclear) y evidence.
result.issues[]Observaciones (por ejemplo, una fecha fuera de plazo).
result.legibilitygood · partial · poor
errorMotivo si status es failed.
usageProveedor y modelo usados, tokens, coste (cost, en la moneda currency) y duración.
callbackEstado de la entrega del callback (solo en modo asíncrono).

Errores

CódigoMotivo
401Falta la clave o no es válida (o ha caducado).
403La clave no tiene el permiso necesario, el cliente está desactivado o no puede usar ese tipo de documento.
404La solicitud no existe o pertenece a otro cliente.
422Datos no válidos: falta el fichero, tipo inexistente, faltan parámetros, URL de callback no válida…
500Error interno. Puedes reintentar.

Ejemplo de 422:

{
  "message": "Faltan parámetros obligatorios para este tipo de documento: cif_entidad.",
  "errors": { "parameters": ["Faltan parámetros obligatorios para este tipo de documento: cif_entidad."] }
}

Los problemas con el documento o con el proveedor de IA (formato imposible de convertir, documento demasiado grande…) no devuelven un error HTTP: la solicitud termina con "status": "failed" y el motivo en error.

Datos y privacidad