API de Transcripción
La API de Transcripción permite extraer texto imágenes/PDF mediante reconocimiento automático (ASR/OCR). Soporta el envío de uno o varios archivos y, cuando corresponde, fusiona automáticamente anverso/reverso de un mismo documento para devolver un único resultado.
Autenticación
Utiliza la cabecera unificada X-CapData-Token. El valor puede ser la API Key de un Propietario, una Agencia o el token de un Agente.
X-CapData-Token: tu_api_key_o_token
Endpoint
POST /api/transcribe
Recibe uno o varios archivos y devuelve sus transcripciones. La petición debe ser
multipart/form-data.
Parámetros (multipart/form-data)
files[](requerido): uno o varios archivos a transcribir.language(opcional): código de idioma preferido (p. ej.es,en). Si se omite, se intenta detección automática.
Ejemplo: un archivo
curl -X POST "https://capdata.es/api/transcribe" \
-H "X-CapData-Token: TU_TOKEN" \
-F "files=@/ruta/llamada_cliente.m4a" \
-F "language=es"
Respuesta (200 OK)
{
"results": [
{
"filename": "llamada_cliente.m4a",
"text": "Transcripción completa de la llamada...",
"language_detected": "es",
"duration_seconds": 184.2,
"merged": false
}
]
}
Ejemplo: fusión anverso/reverso (dos archivos)
Si subes dos archivos que representan las dos caras de un mismo documento (p. ej. anverso/reverso), el sistema intentará fusionarlos automáticamente.
curl -X POST "https://capdata.es/api/transcribe" \
-H "X-CapData-Token: TU_TOKEN" \
-F "files[]=@/ruta/reserva_anverso.pdf" \
-F "files[]=@/ruta/reserva_reverso.pdf"
Respuesta (200 OK) con fusión
{
"results": [
{
"filename": "reserva",
"text": "Texto unificado de anverso y reverso en orden de lectura...",
"language_detected": "es",
"merged": true,
"merged_from": ["reserva_anverso.pdf", "reserva_reverso.pdf"]
}
]
}
_anverso/_reverso, -anverso/-reverso, _front/_back o -front/-back,
el servicio los fusiona en un único resultado. El contenido se concatena en orden lógico (anverso → reverso).
Transcripción Especializada de Facturas
Este servicio automatiza la lectura de facturas, las transforma en datos JSON y gestiona el ciclo de vida del documento y del proveedor asociado.
POST /api/transcribe-invoice
Envía uno o varios archivos de factura para su procesamiento. Esta acción consume 1 token por cada archivo procesado, si es un PDF 1 token por cada página.
Cuerpo de la Petición (multipart/form-data)
La petición debe ser de tipo multipart/form-data. Los archivos deben enviarse bajo la clave files.
curl -X POST "https://capdata.es/api/transcribe-invoice" \
-H "X-CapData-Token: tu_api_key_o_token" \
-F "files=@/ruta/factura_A.pdf" \
-F "files=@/ruta/factura_B.jpg"
Respuesta Exitosa (200 OK)
Devuelve un array resultados con los datos estructurados, una vista previa en base64 y una URL segura para la visualización completa del documento.
{
"resultados": [
{
"source_file": "factura_A.pdf",
"supplier_contact_id": 42,
"supplier_invoice_id": 101,
"data": {
"numero_factura": "FAC-2025-001",
"proveedor_nif": "B12345678",
"total_factura": 121.00,
// ...
},
"preview_image_base64": "data:application/pdf;base64,JVBERi0x...",
"full_view_url": "https://capdata.es/portal/slug-cliente/serve/invoice_blob/101"
}
],
"tokens_remaining": 98
}
Visualización del Documento Original (Flujo en 2 Pasos)
La URL full_view_url devuelta debe ser llamada desde tu backend (server-to-server), añadiendo la misma cabecera de autenticación. Tu servidor recibirá el archivo binario (PDF, JPG) y deberá retransmitirlo (stream) a tu usuario final.
Este patrón de "proxy" es crucial para no exponer nunca tu API Key en el lado del cliente.
Ejemplo Práctico Interactivo
Utiliza esta herramienta para probar el flujo completo de la API de Transcripción de Facturas.
Petición de Transcripción
El siguiente código HTML y JavaScript muestra cómo crear un formulario para que el usuario seleccione una factura, y cómo enviarla a la API de CapData de forma segura.
HTML del Formulario
Crea un formulario simple con un input para el archivo, un botón, y un área para mostrar los resultados.
<!-- Contenedor para la subida de facturas -->
<div>
<label for="invoiceFileInput">Seleccionar Factura:</label>
<input type="file" id="invoiceFileInput" accept=".pdf,.jpg,.jpeg,.png">
<button id="uploadButton">Transcribir Factura</button>
</div>
<!-- Área para mostrar el estado y los resultados -->
<div id="statusArea" style="margin-top: 15px;"></div>
<!-- Contenedor para la respuesta JSON (oculto por defecto) -->
<pre id="responseContainer" style="display:none;"><code class="language-json"></code></pre>
JavaScript (usando `fetch`)
Este script se encarga de escuchar el clic en el botón, construir la petición `multipart/form-data` y manejar la respuesta de la API.
document.addEventListener("DOMContentLoaded", () => {
const fileInput = document.getElementById("invoiceFileInput");
const uploadButton = document.getElementById("uploadButton");
const statusArea = document.getElementById("statusArea");
const responseContainer = document.getElementById("responseContainer");
const jsonCodeBlock = responseContainer.querySelector("code");
// IMPORTANTE: Gestiona tu API Key de forma segura.
// Nunca la expongas directamente en el código del frontend en producción.
const API_KEY = "TU_X-CAPDATA-TOKEN_AQUI";
const API_ENDPOINT = "https://capdata.es/api/transcribe-invoice";
uploadButton.addEventListener("click", async () => {
const file = fileInput.files[0];
if (!file) {
statusArea.textContent = "Por favor, selecciona un archivo primero.";
return;
}
// --- 1. Preparar la petición ---
const formData = new FormData();
formData.append("files", file); // La clave debe ser "files"
// --- 2. Actualizar la UI y realizar la llamada ---
statusArea.textContent = "Transcribiendo, por favor espera...";
uploadButton.disabled = true;
responseContainer.style.display = "none";
try {
const response = await fetch(API_ENDPOINT, {
method: "POST",
headers: {
"X-CapData-Token": API_KEY
},
body: formData
});
const responseData = await response.json();
if (!response.ok) {
// Si la API devuelve un error (4xx, 5xx), lo lanzamos
throw new Error(responseData.error || `Error HTTP: ${response.status}`);
}
// --- 3. Procesar la respuesta exitosa ---
statusArea.textContent = "¡Transcripción completada con éxito!";
jsonCodeBlock.textContent = JSON.stringify(responseData, null, 2);
responseContainer.style.display = "block";
// Opcional: Resaltar la sintaxis si usas Prism.js
if (window.Prism) {
Prism.highlightElement(jsonCodeBlock);
}
// Aquí guardarías `responseData.resultados[0].full_view_url`
// para usarlo en la Fase 2.
} catch (error) {
statusArea.textContent = `Error: ${error.message}`;
console.error("Error en la transcripción:", error);
} finally {
uploadButton.disabled = false;
}
});
});
Fase 2: Visualización Segura (Backend)
Al hacer clic en la imagen de vista previa, se simula el siguiente paso. En una aplicación real, tu frontend no llama
directamente a la full_view_url. En su lugar, llama a un endpoint en tu propio backend, el cual
actúa como un "proxy" para obtener el archivo de forma segura, como se muestra en el siguiente ejemplo de código.
import requests
from flask import Blueprint, Response, abort
erp_bp = Blueprint("erp_routes", __name__)
# Esta función es un placeholder: debes implementar cómo tu ERP
# recupera la URL y la API Key que guardó tras la Fase 1.
def get_capdata_info_from_your_db(invoice_id):
# Lógica para buscar en tu base de datos...
# return {"full_view_url": "...", "api_key": "..."}
pass
@erp_bp.route("/ver-factura-capdata/<int:capdata_invoice_id>")
def ver_factura_capdata(capdata_invoice_id):
"""
Este endpoint actúa como un proxy para obtener de forma segura
un archivo de CapData y servirlo al usuario final.
"""
# 1. Obtener la URL y API Key desde tu base de datos
capdata_info = get_capdata_info_from_your_db(capdata_invoice_id)
if not capdata_info:
abort(404, "Información de la factura no encontrada.")
capdata_url = capdata_info["full_view_url"]
api_key = capdata_info["api_key"]
headers = {"X-CapData-Token": api_key}
try:
# 2. Hacer la petición a CapData en modo "stream"
response_from_capdata = requests.get(
capdata_url, headers=headers, stream=True, timeout=60
)
response_from_capdata.raise_for_status()
# 3. Retransmitir la respuesta (el archivo) al usuario final
return Response(
response_from_capdata.iter_content(chunk_size=8192),
content_type=response_from_capdata.headers["Content-Type"]
)
except requests.exceptions.RequestException as e:
status_code = e.response.status_code if e.response is not None else 503
abort(status_code, "No se pudo obtener el documento desde CapData.")
Formatos y límites
- Formatos admitidos (orientativo): imagen/PDF para OCR (
.pdf,.jpg,.jpeg,.png). Si envías un formato no admitido, obtendrás un error de validación. - Tamaño máximo: si tu despliegue aplica límites, las subidas que superen el máximo devolverán error de validación (
400).
Consumo de tokens y comportamiento
- Consumo: se consume 1 token por archivo procesado. Cuando aplica la fusión anverso/reverso, ambos lados cuentan como un solo archivo lógico y por tanto consumen 1 token en total.
- Saldo: si la operación es exitosa, el saldo actualizado puede incluirse en la respuesta según la configuración del servidor.
Errores y respuestas de fallo
402 Payment Required (tokens insuficientes)
Se devuelve cuando el actor no dispone de saldo suficiente para procesar los archivos solicitados. La respuesta incluye el saldo actual y los tokens necesarios para completar la operación.
{
"error": "Tokens insuficientes.",
"tokens_remaining": 0,
"tokens_needed": 2
}
400 Bad Request (validación)
Se devuelve cuando la petición no cumple las validaciones (por ejemplo: no se envió files,
el tipo de archivo no es válido, el tamaño supera el límite o hay combinaciones inválidas).
{
"error": "Validación fallida.",
"details": [
{"field": "files", "message": "Debes adjuntar al menos un archivo."},
{"field": "files[0]", "message": "Formato no admitido (.exe)."},
{"field": "files[1]", "message": "El tamaño supera el máximo permitido."}
]
}
500 Internal Server Error
Error inesperado en el procesamiento del archivo o en un proveedor externo. Reintenta pasado unos minutos.
{
"error": "Error interno de servidor. Inténtalo de nuevo más tarde."
}
402, 400 y 500 en el cliente.
Muestra mensajes claros y, en caso de 402, ofrece opciones para recargar saldo o reducir el número de archivos.