link

Conexión

Todos nuestros endpoints son accesibles mediante la siguiente URL base:

La API espera el siguiente encabezado de autorización:

NOTA

Las API Keys se gestionan desde el panel de nuestra plataforma.

wrap_text

Formato de respuesta

Todas las respuestas JSON (tanto exitosas como errores) siguen la siguiente estructura envelope:

{
  "response": { ... },                // Contenido específico del endpoint (puede ser null en errores)
  "apiVersion": "1.0",                // Versión actual de la API
  "trackingId": "uuid-del-request",   // Identificador único para seguimiento
  "timeStamp": "2025-02-23T12:34:56Z",// Fecha y hora UTC de la respuesta
  "error": false,                     // Indica si ocurrió un error
  "errorDetail": null                  // Detalle del error (solo cuando error=true)
}
                            

En caso de error, el campo errorDetail contiene:

{
  "code": 400,
  "message": "Descripción del error",
  "stackTrace": null || string
}
                            

El campo response varía según el endpoint; a continuación se detalla su contenido para cada caso.

api

Endpoints disponibles

Método Endpoint Descripción detallada
POST /Document/RedactAsync Procesamiento asíncrono de un PDF
Request: multipart/form-data con:
  • File (PDF, máx. 20 MB)
  • TolerancePercentage (string que represente un decimal de 0 a 1 ej 0.8, opcional)
  • ExpirationHours (entero entre 1 y 24, opcional; por defecto 24)
Respuesta aceptada (202): Envelope con response conteniendo:
{
  "documentId": "guid",
  "status": "Pending | Processing | Completed | Error | Deleted",
  "expiresAt": "2025-03-01T12:00:00Z",
  "statusUrl": "/Document/{id}/Status"
}
El documento se procesa en segundo plano. Puedes consultar su estado mediante la URL proporcionada.
Errores: mismos códigos que el endpoint síncrono, más 400 si la expiración no es válida, siempre dentro del envelope con error: true.
POST /Document/Score Análisis de entidades PII en un PDF (sin redactar)
Request: multipart/form-data con File y opcionalmente MinimunConfidenceScore (string que represente un decimal de 0 a 1 ej 0.8, opcional).
Respuesta exitosa (200): Envelope con response conteniendo la lista de entidades detectadas:
[
  { "entityType": "RUT", "confidence": 0.98, "page": 1, "text": "12.345.678-9" }
]
Errores: similares a /Document/Redact (límite de páginas, archivo inválido, etc.) dentro del envelope.
Nota: Este endpoint también registra el consumo de páginas.
POST /Document/Type Detección del tipo de documento
Request: multipart/form-data con File (PDF).
Respuesta exitosa (200): Envelope con response conteniendo:
{ "documentType": "Invoice | Contract | ..." }
Errores: mismos que otros endpoints de subida, dentro del envelope.
GET /Document/{id}/Status Consulta el estado de un documento asíncrono
Parámetros de ruta: id (GUID del documento).
Respuesta (200): Envelope con response conteniendo:
{
  "documentId": "guid",
  "status": "Pending | Processing | Completed | Error | Deleted | NotFound",
  "statusMessage": "mensaje opcional",
  "expiresAt": "2025-03-01T12:00:00Z"
}
Si el documento no existe o no pertenece al cliente autenticado, response.status será "NotFound".
DELETE /Document/{id}/Remove Elimina un documento y sus artefactos
Parámetros de ruta: id (GUID).
Respuesta (200): Envelope con response conteniendo:
{
  "documentId": "guid",
  "status": "Deleted",
  "statusMessage": "Documento eliminado correctamente",
  "expiresAt": null
}
Si no se encuentra o no pertenece al cliente, response.status será "NotFound".
GET /Document/{id}/Download Descarga el documento procesado (redactado)
Parámetros de ruta: id (GUID).
Comportamiento:
  • Si el documento está Completed: retorna el archivo PDF directamente (sin envoltorio JSON).
  • En cualquier otro estado (Pending, Processing, Error, Deleted, NotFound): retorna un envelope con response conteniendo:
    {
      "status": "Pending",
      "message": "Documento en cola de procesamiento"
    }
Nota: Si el documento no existe o no pertenece al cliente, el status será "NotFound".
GET /Document/ActiveDocuments/{page}/{pageSize} Lista paginada de documentos activos (no eliminados ni expirados)
Parámetros de ruta: page (int ≥ 1), pageSize (int, máx. 100).
Respuesta (200): Envelope con response conteniendo:
{
  "documents": [
    { "documentId": "guid", "fileName": "...", "pageCount": 5, "status": "...", "createdAt": "...", "expiresAt": "...", "processingTimeMinutes": 2.5 }
  ],
  "page": 1,
  "pageSize": 10,
  "total": 42
}
Los documentos se ordenan por fecha de creación descendente.
GET /Document/ConsumptionHistory Historial de consumo de páginas por mes
Respuesta (200): Envelope con response conteniendo lista de objetos:
[
  {
    "clientId": 123,
    "businessName": "Empresa SA",
    "taxId": "30-12345678-9",
    "periodMonth": "2025-02",
    "totalPagesProcessed": 150,
    "totalRequests": 12,
    "synchronousPages": 100,
    "asynchronousPages": 50
  }
]
Muestra el desglose mensual de páginas procesadas (síncronas y asíncronas).
GET /Document/CurrentMonthConsumption Consumo y límites del mes actual
Respuesta (200): Envelope con response conteniendo:
{
  "clientId": 123,
  "businessName": "Empresa SA",
  "taxId": "30-12345678-9",
  "monthlyPageLimit": 500,
  "bonusPagesAccumulated": 10,
  "pagesConsumed": 350,
  "availableMonthlyPages": 150,
  "totalAvailablePages": 160,
  "requestCount": 25,
  "periodMonth": "2025-02"
}
Indica cuántas páginas se han consumido en el mes, el límite, páginas de bono y disponibles.
webhook

Webhook de notificación

Una vez que el procesamiento asíncrono de un documento finaliza (con estado Completed, Error o Deleted), nuestro sistema enviará automáticamente una solicitud HTTP POST a la URL que hayas configurado en el panel de administración. Esto te permite reaccionar en tiempo real al resultado sin necesidad de realizar consultas periódicas (polling).

Formato del payload enviado
{
    "ClientId": "uuid-del-cliente",
    "DocumentId": "uuid-del-documento",
    "Status": "Completed | Error | Deleted",
    "ExpiresAt": "2025-03-01T12:00:00Z",
    "StatusMessage": "Mensaje opcional (ej. error detallado)",
    "TimeStamp": "1710000000",  // Unix timestamp en segundos
    "Hash": "Base64(HMAC-SHA256 de TimeStamp con MasterKey)"
}
                            
Seguridad y autenticidad

Para garantizar que la petición realmente proviene de nuestros servidores y no de un tercero, cada webhook incluye un campo Hash. Este hash se genera aplicando HMAC-SHA256 sobre el valor del campo TimeStamp (en bytes UTF-8) utilizando la Master Key (también en bytes UTF-8) que puedes configurar en el panel. Debes calcular el hash en tu servidor y compararlo con el recibido; si coinciden, puedes confiar en que la petición es legítima.

Comportamiento esperado de tu endpoint

Tu endpoint debe responder con un código HTTP 200 OK lo antes posible para confirmar la recepción. Nosotros no procesamos el contenido de la respuesta; simplemente necesitamos saber que el mensaje llegó. Si no recibimos un 200, el sistema reintentará el envío hasta 10 veces adicionales.

Nota: El timeout de la solicitud es de 30 segundos. Asegúrate de que tu endpoint responda rápidamente para evitar reintentos innecesarios.

Ejemplo de implementación (Node.js - Express)
app.post('/webhook', (req, res) => {
    const { ClientId, DocumentId, Status, ExpiresAt, StatusMessage, TimeStamp, Hash } = req.body;

    // 1. Obtener la Master Key desde las variables de entorno
    const masterKey = process.env.WEBHOOK_MASTER_KEY;

    // 2. Convertir a bytes (UTF-8)
    const keyBytes = Buffer.from(masterKey, 'utf8');
    const timeBytes = Buffer.from(TimeStamp, 'utf8');

    // 3. Calcular HMAC-SHA256
    const hmac = crypto.createHmac('sha256', keyBytes);
    hmac.update(timeBytes);
    const computedHash = hmac.digest().toString('base64');

    // 4. Comparar ambos hashes
     if (computedHash !== Hash) {
        return res.status(401).send('Firma inválida');
    }

    // 5. Procesar según el estado (opcional)
    console.log(`Documento ${DocumentId} para cliente ${ClientId} cambió a ${Status}`);

    // 6. Responder inmediatamente
    res.status(200).send('OK');
});