Saptiva|API Reference
Postman Collection

Saptiva Document Extraction API

La API de Extracción de Documentos de Saptiva permite subir documentos PDF para extracción de datos potenciada por IA. Soporta cuatro tipos de documentos: Contratos, Pólizas de Seguro, Fianzas y Actas Constitutivas.

Base URL

https://adjudicator.saptiva.com

Autenticación

Todos los endpoints requieren autenticación mediante API key a través del header Authorization.

Header de autenticación
Authorization: Token <sk-saptiva-your_api_key>

Errores

La API utiliza códigos de estado HTTP convencionales para indicar el éxito o fracaso de una petición. Todas las respuestas de error incluyen un mensaje legible (error) y un código máquina (code).

Formato de respuesta de error
{
  "error": "Mensaje de error legible",
  "code": "ERROR_CODE"
}
HTTPCódigoDescripción
400MISSING_FIELDSCampos requeridos faltantes
400INVALID_DOCUMENT_TYPEtipo_documento debe ser 1, 2, 3 o 4
400INVALID_FILE_FORMATSolo se aceptan archivos PDF
400FILE_TOO_LARGEEl archivo excede el límite de tamaño
400PDF_CORRUPTEDPDF corrupto, sin páginas o estructura ilegible
400TEXT_EXTRACTION_FAILEDNo se pudo extraer texto del documento
401AUTH_MISSING_HEADERFalta el header Authorization
401AUTH_INVALID_FORMATFormato inválido (debe ser: Token <key>)
401AUTH_EMPTY_KEYAPI key vacía
401AUTH_INVALID_KEYAPI key inválida o inactiva
404JOB_NOT_FOUNDJob no encontrado
409DUPLICATE_DOCUMENTYa existe documento con mismo expediente_id + documento_id
500OCR_START_FAILEDFalló al iniciar procesamiento OCR
500INTERNAL_ERRORError interno del servidor

Error de PDF Corrupto (400)

Se retorna cuando el archivo PDF está dañado o es ilegible. El archivo no se sube al almacenamiento y no se crea un registro de job.

Respuesta de error
{
  "error": "Archivo PDF corrupto: PDF file has no pages or structure is corrupted",
  "code": "PDF_CORRUPTED"
}

Posibles errores de validación:

  • PDF has no readable pages - El PDF tiene 0 páginas legibles
  • PDF structure is invalid - La estructura interna del PDF no se puede parsear
  • PDF pages cannot be read - Las páginas existen pero el contenido es inaccesible
  • PDF file is corrupted or unreadable - Corrupción general detectada

Webhook de error:

Si se proporcionó webhook_url, se envía un webhook de error con id igual a documento_id (no tracker_id, ya que no se crea un job):

PUT webhook_url
{
  "exitoso": false,
  "id": "DOC-001",
  "tipo_documento": 1,
  "texto_extraido": null,
  "datos_extraidos": null,
  "mensaje": "Archivo PDF corrupto: PDF file has no pages or structure is corrupted",
  "code": "PDF_CORRUPTED"
}

Extracción

POST/api/extract/

Extraer documento

Sube un documento PDF para extracción. El contenido debe ser multipart/form-data.

Parámetros de Request

CampoTipoRequeridoDescripción
documento_idstringTu ID interno de seguimiento del documento
expediente_idstringID del caso/expediente al que pertenece el documento
documentofileArchivo PDF a procesar
webhook_urlstringNoURL para recibir resultados vía PUT. Si se omite, usar GET para obtener resultados.
tipo_documentonumber1=Contrato, 2=Seguro, 3=Fianza, 4=Acta Constitutiva
GET/api/extract/{tracker_id}

Obtener resultado

Recupera el estado y resultados de un job de extracción. Útil si tu webhook falló o para consultar el estado.

Valores de Status

StatusDescripción
receivedJob creado, procesamiento no iniciado
ocr_processingEsperando OCR de DocumentAI
llm_processingEjecutando extracción LLM
completedProcesado exitosamente
webhook_failedProcesado pero falló la entrega del webhook
errorEl procesamiento falló
POST/api/extract/reprocess

Reprocesar documento

Re-ejecuta la extracción LLM en un documento previamente subido. El contenido debe ser application/json.

Request Body

JSON
{
  "tracker_id": "saptiva_1704825600000_abc123"
}
GET/api/extract/jobs

Listar jobs

Lista todos los jobs de extracción para la API key autenticada. Útil para obtener tracker IDs y consultar resultados.

Respuesta

CampoDescripción
jobsArray de jobs con tracker_id, status, documento_id, etc.
totalNúmero total de jobs
GET/api/extract/usage

Obtener uso

Obtiene estadísticas de uso para la API key autenticada. Útil para facturación y seguimiento.

Campos de Respuesta

CampoDescripción
total_pagesSuma de todas las páginas procesadas (incluye reprocesamiento)
total_processing_eventsNúmero total de eventos de procesamiento
unique_documentsNúmero de documentos únicos procesados
documentsArray con detalles de cada documento y su processing_count

Webhook Callback

Cuando el procesamiento se completa (sync o async), se envía un request PUT a tu webhook_url.

Payload de Éxito

PUT webhook_url
{
  "exitoso": true,
  "id": "saptiva_1704825600000_abc123",
  "tipo_documento": 1,
  "texto_extraido": "CONTRATO DE SERVICIOS COMERCIALES...\nCLÁUSULA PRIMERA...",
  "datos_extraidos": {
    "numero_contrato": {
      "value": "AIFA-DCS-SSC-GSC-001-2025",
      "certainty": 1.0
    },
    "nombre_contraparte": {
      "value": "Empresa ABC S.A. de C.V.",
      "certainty": 1.0
    },
    "rfc_contraparte": {
      "value": "EAB123456XX0",
      "certainty": 0.8
    },
    "monto_afianzado": {
      "value": {
        "monto": 1500000,
        "porcentaje_aplicado": 10,
        "base_calculo": "10% del monto total del contrato",
        "moneda": "MXN"
      },
      "certainty": 1.0
    },
    "errors": {
      "hasError": false,
      "errorMessage": ""
    }
  },
  "mensaje": "Documento procesado exitosamente"
}

Payload de Error

PUT webhook_url
{
  "exitoso": false,
  "id": "saptiva_1704825600000_abc123",
  "tipo_documento": 1,
  "texto_extraido": null,
  "datos_extraidos": null,
  "mensaje": "Error procesando documento: No text extracted from document",
  "code": "TEXT_EXTRACTION_FAILED"
}

Códigos de error en webhooks: El campo code contiene los mismos códigos listados en la tabla de errores (ej. PDF_CORRUPTED, TEXT_EXTRACTION_FAILED, OCR_START_FAILED, INTERNAL_ERROR).

Tipos de Documento

tipo_documento = 1

Contrato (AIFA)

Contratos del Aeropuerto Internacional Felipe Ángeles. Detecta automáticamente el tipo de contrato según el patrón del número:

AIFA-DCS-SSC-GSC-XXX-YYYY

Servicios Comerciales

AIFA-DCS-SMC-...

Movilidad

AIFA-DCS-SSAC-...

Servicios Aeroportuarios

AIFA-C-[LPN|ITP|AD]-...

Recursos Materiales

Campos Extraídos

tipo_contrato_detectadoTipo de contrato detectado
numero_contratoNúmero completo del contrato
desglose_numero_contratoDesglose (entidad, dirección, subdirección, etc.)
tipo_contratoTipo (arrendamiento, prestación de servicios, etc.)
nombre_aeropuertoAeropuerto Internacional Felipe Ángeles, S.A. de C.V.
rfc_aeropuertoAIF2101127VA
nombre_contraparteNombre de la contraparte
rfc_contraparteRFC de la contraparte
rol_contraparteRol (arrendatario, prestador, proveedor, transportista)
clausula_objetoCláusula del objeto del contrato
clausula_uso_destinoCláusula de uso exclusivo
espacio_arrendadoEspacio (metros, ubicación, nomenclatura)
vigenciaDuración (fecha_inicio, fecha_termino, años)
importeDetalles (renta_mensual, contraprestación, monto_contrato)
cuota_serviciosCuota de servicios (porcentaje)
clausula_garantia_fianzaCláusula de garantía/fianza
monto_afianzadoMonto (monto, porcentaje, base_calculo, moneda)
declaraciones_fianzaRequisitos de póliza de fianza (array)
naturaleza_obligacionesDivisible/indivisible
clausula_seguroCláusula de seguro
declaraciones_seguroRequisitos de póliza de seguro (array)
jurisdiccionJurisdicción (para Recursos Materiales)
fecha_firmaFecha de firma
tipo_documento = 2

Seguro

Pólizas de seguro para AIFA.

Campos Extraídos

numero_polizaNúmero de póliza (numérico o alfanumérico)
nombre_aseguradoNombre del asegurado (puede ser empresa matriz o subsidiaria)
vigenciaPeríodo de cobertura (fecha_inicio, fecha_fin, descripcion)
ubicacion_riesgoUbicación del riesgo (dirección del aeropuerto)
coberturasArray de coberturas (tipo, descripcion, suma_asegurada, deducible)
clausula_beneficiarioCláusula del beneficiario (nombre_beneficiario, descripcion)
clausulas_polizaCláusulas de la póliza del contrato (array)
aseguradoraNombre de la compañía aseguradora
tipo_documento = 3

Fianza

Fianzas de garantía para contratos AIFA.

Campos Extraídos

numero_fianzaNúmero de fianza (puede estar ausente en borradores)
nombre_fiadoNombre del fiado/contraparte
nombre_beneficiarioNombre del beneficiario (típicamente AIFA)
entidad_contratoEntidad que genera el contrato
domicilio_beneficiarioDirección del beneficiario (opcional)
numero_contratoNúmero del contrato garantizado
fecha_contratoFecha de firma del contrato
revision_objetoRevisión del objeto (tipo_contrato, descripcion, dimensiones, nomenclatura)
vigenciaPeríodo de validez (fecha_inicio, fecha_fin, descripcion)
monto_afianzadoMonto afianzado (monto, moneda, descripcion)
declaracionesDeclaraciones de la fianza (array)
afianzadoraNombre de la compañía afianzadora
tipo_fianzaTipo (cumplimiento, anticipo, vicios ocultos, fidelidad, etc.)
tipo_documento = 4

Acta Constitutiva

Actas constitutivas (documentos de constitución de empresas) emitidas por notarios mexicanos.

Campos Extraídos

fecha_constitucionFecha de constitución de la empresa (DD-MM-YYYY)
razon_socialRazón social incluyendo tipo societario (S.A. de C.V., S. de R.L., etc.)
poderes_administracionLista de personas con poderes de administración (array)
poderes_dominioLista de personas con poderes de dominio (array)
ambos_poderesLista de personas con ambos poderes (administración y dominio)
lista_accionistasLista de todos los accionistas (array)
participacion_accionariaPorcentajes de participación por accionista (array: "Nombre: Porcentaje%")
clausula_exclusion_extranjerosCláusula de exclusión de extranjeros: "True" (excluye), "False" (permite), "Null" (sin cláusula)

Formato de Datos

Fechas

Todas las fechas se devuelven en formato DD-MM-YYYY.

Montos

Los montos monetarios se devuelven como números (no como strings).

Certainty Scores

Cada campo extraído incluye un score de certainty de 0.0 a 1.0 indicando el nivel de confianza:

1.0Encontrado explícitamente en el documento
0.8Inferido con alta confianza
0.5Incierto/coincidencia parcial
0.0No encontrado en el documento

Texto Extraído

El campo texto_extraido contiene el texto completo del documento como texto plano.

Límites

Tamaño máximo de archivo100MB
Timeout de webhook30 segundos

Ejemplos de Integración

Cliente Python

Python
import requests

API_KEY = "sk-saptiva-your-key-here"
BASE_URL = "https://adjudicator.saptiva.com"

def extract_document(pdf_path, documento_id, expediente_id, tipo_documento, webhook_url):
    with open(pdf_path, 'rb') as f:
        response = requests.post(
            f"{BASE_URL}/api/extract/",
            headers={"Authorization": f"Token {API_KEY}"},
            files={"documento": f},
            data={
                "documento_id": documento_id,
                "expediente_id": expediente_id,
                "tipo_documento": tipo_documento,
                "webhook_url": webhook_url
            }
        )
    return response.json()

# Subir un contrato
result = extract_document(
    pdf_path="contract.pdf",
    documento_id="DOC-001",
    expediente_id="EXP-2026-001",
    tipo_documento=1,  # Contrato
    webhook_url="https://my-server.com/webhook"
)
print(f"Tracker ID: {result['tracker_id']}")

# Subir un acta constitutiva
result = extract_document(
    pdf_path="acta_constitutiva.pdf",
    documento_id="DOC-002",
    expediente_id="EXP-2026-001",
    tipo_documento=4,  # Acta Constitutiva
    webhook_url="https://my-server.com/webhook"
)
print(f"Tracker ID: {result['tracker_id']}")

Receptor de Webhook (Flask)

Python (Flask)
from flask import Flask, request, jsonify

app = Flask(__name__)

@app.route('/webhook/receive', methods=['PUT'])
def receive_webhook():
    data = request.json

    if data.get('exitoso'):
        tracker_id = data['id']
        tipo = data['tipo_documento']
        extracted = data['datos_extraidos']

        # Procesar los datos extraídos
        print(f"Documento {tracker_id} procesado exitosamente")
        print(f"Campos extraídos: {list(extracted.keys())}")
    else:
        print(f"Procesamiento fallido: {data['mensaje']}")

    return jsonify({"status": "received"}), 200

if __name__ == '__main__':
    app.run(port=5001)