Documentación de la API
Con la API de DokuScan subes documentos mediante programa, obtienes los resultados y recibes avisos por webhook. El acceso forma parte del paquete Business.
Contenido
- Resumen
- Autenticación
- Subir un documento
- Obtener escaneos
- Listar plantillas
- Consultar el saldo
- Webhooks
- Códigos de error
Resumen
- URL base: https://app.dokuscan.com/api/v1
- Las respuestas son JSON. Envía la cabecera Accept: application/json para que los errores también lleguen como JSON.
- Las marcas de tiempo usan el formato ISO 8601.
- El acceso forma parte del paquete Business (a partir de 2.000 escaneos).
Autenticación
Crea las claves de API en la aplicación, en "API y webhooks" (hasta 5 claves). Una clave solo se muestra completa al crearla. Envíala en cada solicitud como token Bearer o en la cabecera X-API-Key.
curl https://app.dokuscan.com/api/v1/account \
-H "Authorization: Bearer dsk_xxxxxxxx" \
-H "Accept: application/json"
Si falta la clave o no es válida, la API responde con 401. Si la cuenta no tiene el paquete Business, responde con 403.
Subir un documento
POST /scans crea un escaneo. El procesamiento se ejecuta en segundo plano. El resultado se obtiene con GET /scans/{id} o se recibe por webhook.
| Parámetro | Descripción |
|---|---|
| template_id (obligatorio) | ID de la plantilla, véase GET /templates. |
| file (obligatorio) | Archivo como multipart/form-data. PDF, JPG, JPEG o PNG, hasta 20 MB. |
curl -X POST https://app.dokuscan.com/api/v1/scans \
-H "Authorization: Bearer dsk_xxxxxxxx" \
-H "Accept: application/json" \
-F "template_id=12" \
-F "file=@lieferschein.pdf"
Respuesta en caso de éxito (201):
{
"id": 4711,
"status": "uploaded",
"template_id": 12,
"filename": "lieferschein.pdf",
"created_at": "2026-10-18T09:15:00+00:00"
}
Un escaneo consume un crédito de escaneo. Los PDF de varias páginas no se dividen a través de la API; el archivo cuenta como un escaneo.
Obtener escaneos
GET /scans/{id} devuelve un escaneo. GET /scans devuelve una lista, de más reciente a más antiguo, con 25 entradas por página. Filtros: status, template_id. Página: page.
{
"id": 4711,
"status": "completed",
"filename": "lieferschein.pdf",
"template_id": 12,
"confidence": 92,
"extracted_data": {
"lieferant": "Baustoffe Huber GmbH",
"datum": "12.10.2026",
"rows": [
{ "artikel": "Zement CEM II 25 kg", "menge": 40, "einheit": "Sack" },
{ "artikel": "Kantholz 8x10 cm", "menge": 24, "einheit": "Stk" }
]
},
"reviewed_data": null,
"validation_errors": null,
"is_partial": false,
"error_message": null,
"created_at": "2026-10-18T09:15:00+00:00",
"updated_at": "2026-10-18T09:15:21+00:00"
}
| Estado | Significado |
|---|---|
| uploaded | Subido, a la espera de procesamiento. |
| processing | En procesamiento. |
| completed | Terminado, extracted_data está rellenado. |
| validation_failed | Terminado, pero se infringió al menos una regla de validación. Los datos están disponibles; los detalles figuran en validation_errors. |
| failed | Fallido, el crédito se devuelve. error_message indica la causa. |
La estructura de extracted_data sigue los campos de la plantilla: los valores individuales están bajo la clave del campo y las filas de tabla como lista bajo rows o bajo la clave del campo de tabla. reviewed_data contiene los valores corregidos manualmente si el escaneo se revisó. confidence es un valor de 0 a 100 e is_partial marca un resultado parcial.
Listar plantillas
GET /templates devuelve las plantillas activas de la cuenta. extraction_mode es scalar, tabular, mixed o vision.
{
"data": [
{
"id": 12,
"name": "Lieferschein Huber",
"extraction_mode": "mixed",
"usage_count": 58,
"created_at": "2026-09-20T10:02:11.000000Z"
}
]
}
Consultar el saldo
GET /account devuelve el saldo de escaneos restante.
{
"remaining_scans": 1842,
"total_scans": 2000,
"used_scans": 158
}
Webhooks
Los webhooks avisan a tu aplicación en cuanto un escaneo termina o falla. Puedes crearlos en la aplicación, en "API y webhooks", o mediante la API: GET /webhooks, POST /webhooks (name, url, events), DELETE /webhooks/{id} y GET /webhooks/{id}/logs.
| Evento | Cuándo |
|---|---|
| scan.completed | Se procesó un escaneo (estado completed o validation_failed). |
| scan.failed | El procesamiento falló. |
- La URL debe ser una dirección HTTPS accesible públicamente.
- DokuScan envía un POST con cuerpo JSON y espera hasta 10 segundos. Cualquier estado 2xx se considera correcto.
- Tras 10 fallos seguidos, el webhook se desactiva automáticamente.
- Al crear el webhook se muestra un secreto. Solo se muestra completo una vez.
Ejemplo de cuerpo para scan.completed:
{
"event": "scan.completed",
"timestamp": "2026-10-18T09:15:21+00:00",
"data": {
"scan_id": 4711,
"filename": "lieferschein.pdf",
"template_id": 12,
"status": "completed",
"confidence": 92,
"extracted_data": { "lieferant": "Baustoffe Huber GmbH", "rows": [] },
"validation_errors": [],
"is_partial": false
}
}
Verificar la firma
Cada solicitud incluye las cabeceras X-DokuScan-Event y X-DokuScan-Signature. La firma es sha256= más el HMAC-SHA256 del cuerpo sin procesar de la solicitud con tu secreto. Verifícala antes de procesar el contenido.
// PHP
$raw = file_get_contents('php://input');
$expected = 'sha256=' . hash_hmac('sha256', $raw, $secret);
if (!hash_equals($expected, $_SERVER['HTTP_X_DOKUSCAN_SIGNATURE'] ?? '')) {
http_response_code(401);
exit;
}
// Node.js (Express, raw body)
const crypto = require('crypto');
const expected = 'sha256=' + crypto.createHmac('sha256', secret).update(rawBody).digest('hex');
const valid = crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(req.get('X-DokuScan-Signature') || ''));
Códigos de error
| Código | Significado |
|---|---|
| 401 | Falta la clave de API o no es válida. |
| 402 | Crédito de escaneos insuficiente. |
| 403 | La cuenta no tiene acceso a la API (paquete Business). |
| 404 | Escaneo o plantilla no encontrados. |
| 409 | Este archivo ya se subió con esta plantilla. |
| 422 | Parámetros no válidos. La respuesta indica los campos afectados. |
| 429 | Más de 200 escaneos en la última hora. |
| 500 | Error interno. Repite la solicitud más tarde. |
Los errores tienen la forma {"error": "…"}. En 404 y 422 la respuesta contiene el campo message y, en 422, además errors.