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.
https://app-qqqarmamumfa68y4hmgskosj.launchlane.space/api/v1Todas 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:
| Permiso | Permite |
|---|---|
documents:validate | POST /validations |
validations:read | GET /validations/{id} |
document-types:read | GET /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
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
Campos
| Campo | Tipo | Descripción |
|---|---|---|
file * | fichero | El documento. Usa file o files[]. |
files[] * | ficheros | Varios ficheros que forman un único documento (por ejemplo, una foto de cada página). Máximo 30. |
document_type * | texto | Código del tipo de documento esperado. |
parameters | objeto | Valores 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_reference | texto | Tu identificador (expediente, solicitud…). Se devuelve en la respuesta. Máx. 191 caracteres. |
callback_url | URL | Si 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
| Formato | Tratamiento |
|---|---|
Las 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, WEBP | Se 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. |
| Otros | Se 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:
| Cabecera | Valor |
|---|---|
Content-Type | application/json |
X-Event | validation.completed |
X-Validation-Id | El id de la solicitud |
X-Signature | t=<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])
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).
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
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
| Campo | Descripción |
|---|---|
id | Identificador único de la solicitud. |
status | pending · processing · completed · failed |
mode | sync o async |
is_valid / verdict | true/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ó. |
confidence | Seguridad del veredicto, de 0 a 1. |
document_type | Tipo esperado y versión de la especificación usada. |
files | Número de ficheros, tamaño total y formatos detectados. |
processing_notes | Conversiones aplicadas (compresión, conversión a PDF o JPEG…). |
result.is_expected_type | Si el documento es del tipo esperado. |
result.detected_document_type | Qué documento parece ser realmente. |
result.summary | Motivo 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.legibility | good · partial · poor |
error | Motivo si status es failed. |
usage | Proveedor y modelo usados, tokens, coste (cost, en la moneda currency) y duración. |
callback | Estado de la entrega del callback (solo en modo asíncrono). |
Errores
| Código | Motivo |
|---|---|
401 | Falta la clave o no es válida (o ha caducado). |
403 | La clave no tiene el permiso necesario, el cliente está desactivado o no puede usar ese tipo de documento. |
404 | La solicitud no existe o pertenece a otro cliente. |
422 | Datos no válidos: falta el fichero, tipo inexistente, faltan parámetros, URL de callback no válida… |
500 | Error 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
- No se guardan los ficheros. En modo síncrono solo existen en memoria durante el análisis. En modo asíncrono se guardan cifrados en una carpeta temporal y se borran en cuanto se procesan.
- No se guardan los valores de
parameters, solo sus nombres. - Se guardan los metadatos: tipo y versión de la especificación, número, tamaño y formato de los ficheros, veredicto, confianza, consumo, tiempos y estado del callback.
- Durante la fase de evaluación también se guarda la respuesta de la IA (
result), que puede incluir datos leídos del documento.