Dátil API de validación

API de validación de documentos XML

Validación de comprobantes electrónicos SRI (Ecuador) y SUNAT (Perú) en una llamada HTTP. Stateless — no se almacena el XML.

  • 1
    Esquema XSD oficial del SRI y SUNAT.
  • 2
    Firma XAdES, cadena de confianza y vencimiento del certificado.
  • 3
    Reglas de negocio: Ficha Técnica SRI v2.32, Reglas SUNAT v2.0.
  • 4
    Cálculos tributarios: catálogo IVA/IGV e identidad valor = base × tarifa/100.
  • 5
    Estado fiscal: autorizacionComprobante (SRI), validarcomprobante (SUNAT).
  • 6
    Estado del emisor: catastro obtenerPorNumerosRuc (SRI), estado_ruc y cond_domicilio (SUNAT).
Ejemplo rápidocURL
curl https://detecno.datil.tax/v1/validations \
  -H "Authorization: Bearer sk_live_..." \
  -H "Content-Type: application/json" \
  -d '{
    "country": "ec",
    "xml": "<factura ...>...</factura>"
  }'

Validaciones

Cada capa se ejecuta de forma independiente: una puede fallar sin detener a las demás, y la respuesta detalla el resultado de cada una por separado.

Parseo XML

Verifica que el documento sea XML bien formado y que la codificación declarada coincida con el contenido. La resolución de entidades externas y la carga de DTDs remotos están deshabilitadas.

Esquema XSD

Valida el documento contra los XSD oficiales publicados por el SRI (Ecuador) y la SUNAT (Perú). Cada error incluye la línea, el código y el mensaje del validador, sin reescritura.

Firma electrónica (XAdES-BES)

Comprueba la presencia de ds:Signature, la integridad del digest, la cadena de confianza hasta una autoridad de certificación reconocida, y la vigencia del certificado al momento de la fecha_emision del comprobante (no al momento del request).

Reglas de negocio

Ecuador: Ficha Técnica del SRI v2.32 — clave de acceso, dígito verificador módulo 11, secuencial y campos obligatorios por tipo de comprobante. Perú: Reglas SUNAT v2.0 sobre UBL 2.1. Cada falla incluye un código, una ruta XPath y un mensaje.

Cálculos tributarios

Esta capa cubre únicamente el IVA (código 2 del catálogo del SRI). Para cada nodo <impuesto> con codigo=2 se verifica:

  • El codigoPorcentaje existe en el catálogo 24 vigente.
  • La tarifa declarada coincide con la del catálogo.
  • valor = baseImponible × tarifa / 100, con tolerancia de redondeo.
  • A nivel documento, importeTotal ≈ totalSinImpuestos + Σ impuestos.

Los demás impuestos (ICE código 3, IRBPNR código 5, entre otros) se ignoran en esta capa: no se les aplica la verificación aritmética y no generan warnings ni errores. Si el documento usa un codigoPorcentaje de IVA que no está en el catálogo, se emite un warning (EC-IVA-CODE-UNKNOWN); la validación no falla por sí sola.

Estado fiscal

Consulta en línea a la autoridad emisora. Ecuador: autorizacionComprobante del SRI con la clave de acceso de 49 dígitos. Perú: validarcomprobante de la SUNAT con RUC, tipo, serie, número, fecha y monto. Si la consulta falla por timeout o indisponibilidad de la autoridad, el error se reporta bajo tax_status.error y el resto del veredicto no se ve afectado.

Estado del emisor

Estado del RUC del emisor en ambos países. Ecuador: consulta independiente al catastro del SRI (obtenerPorNumerosRuc) — devuelve ACTIVO, SUSPENDIDO, PASIVO o BAJA DEFINITIVA. Perú: los campos estado_ruc y cond_domicilio ya forman parte de la respuesta de validarcomprobante, por lo que no se hace una segunda consulta.

Una capa por campoJSON
{
  "valid": true,
  "parsed":        true,
  "schema":        { "ok": true, "errors": [] },
  "signature":     { "present": true,
                     "integrity_ok": true,
                     "chain_trusted": true },
  "rules":         { "ok": true,
                     "errors": [], "warnings": [] },
  "calculations":  { "ok": true, "errors": [] },
  "tax_status":    { "accepted": true,
                     "state": "AUTORIZADO" },
  "issuer_status": { "status": "ACTIVO" }
}
Falla parcial — el resto sigue informando
# Documento bien formado, firma sin cadena confiable,
# autorizado por SRI igualmente.
parsed        ✓
schema        ✓
signature     ✗   chain_trusted: false
rules         ✓
calculations  ✓
tax_status    ✓   AUTORIZADO
issuer_status ✓   ACTIVO

valid         ✗   # porque la firma falló

Autenticación

Bearer token en la cabecera Authorization. Prefijos sk_live_ (producción) y sk_test_ (pruebas). HTTPS obligatorio.

Cabecera de autenticaciónHTTP
Authorization: Bearer sk_live_4f8a...c21b
Respuesta no autenticadaJSON
{
  "error": {
    "type": "authentication_error",
    "code": "missing_api_key",
    "message": "No se proporcionó un API key."
  }
}

URL base

https://detecno.datil.tax

EndpointsREST
POST /v1/validations    # Validar un documento XML

Convenciones

  • JSON, UTF-8. Content-Type y Accept: application/json.
  • Fechas. ISO 8601 UTC (2026-05-06T14:23:11Z).
  • Montos. Decimales en la moneda del documento, sin separadores de miles.
  • Idempotente salvo cambios de estado en SRI/SUNAT.
  • request_id en cada respuesta para soporte.
Cabeceras estándarHTTP
Content-Type: application/json; charset=utf-8
Accept: application/json
Authorization: Bearer sk_live_...
POST /v1/validations

Validar un documento

Recibe un XML, devuelve un objeto validation.

Cuerpo de la solicitud

CampoTipoDescripción
xml requeridostringXML literal o base64 (ver encoding).
countrystringDetectado del XML. Forzar con ec, pe.
encodingstringutf8 (por defecto), base64.
checksarrayformat, signature, calculations, tax_status, issuer_status. Vacío = todas las aplicables.
SolicitudcURL
curl https://detecno.datil.tax/v1/validations \
  -H "Authorization: Bearer sk_live_..." \
  -H "Content-Type: application/json" \
  -d '{
    "country": "ec",
    "xml": "<factura ...>...</factura>",
    "encoding": "utf8"
  }'
Respuesta200 OK
{
  "object": "validation",
  "request_id": "req_01HXY2K9P3M7T8B",
  "country": "ec",
  "valid": true,
  "validated_at": "2026-05-06T14:23:11Z",
  "document": {
    "type":        "invoice",
    "serial":      "001-001-000000123",
    "issue_date":  "06/05/2026",
    "environment": "live",
    "issuer": {
      "id":   "1790000001001",
      "name": "EMPRESA DEMO ECUADOR S.A."
    },
    "signature": {
      "signed_at":   "2026-05-06T20:32:16-05:00",
      "signer":      "JUAN PEREZ EJEMPLO",
      "certificate": {
        "serial":              "01:23:45:67",
        "subject": {
          "common_name":         "JUAN PEREZ EJEMPLO",
          "id_number":           "0900000001",
          "organization":        "EMPRESA DEMO ECUADOR S.A.",
          "organizational_unit": "FACTURACION ELECTRONICA",
          "country":             "EC",
          "locality":            "QUITO"
        },
        "issuer": {
          "common_name":         "AC DEMO CERTIFICADORA",
          "organization":        "DEMO CERTIFICADORA S.A.",
          "organizational_unit": "AUTORIDAD DE CERTIFICACION",
          "country":             "EC",
          "locality":            "QUITO"
        },
        "not_before":          "2026-03-27T20:47:06Z",
        "not_after":           "2028-03-27T21:17:05Z",
        "signature_algorithm": "SHA-256 with RSA",
        "key_usage":           ["Digital Signature", "Non Repudiation"],
        "subject_alt_names":   ["Email: firmas@empresa-demo.example"],
        "crl_urls":            ["http://crl.example.com/demo.crl"],
        "ocsp_urls":           ["http://ocsp.example.com"],
        "policies":            ["1.3.6.1.4.1.99999.1.1"],
        "thumbprint_sha256":   "00:11:22:33:44:55:66:77:..."
      }
    },
    "tax_status": {
      "authority": "sri",
      "status": {
        "accepted": true,
        "code":     "AUTORIZADO",
        "label":    "Autorizado"
      },
      "authorized_at": "2026-05-06T20:32:16-05:00",
      "number":        "0605202601179000000100110010010000001231234567812",
      "environment":   "live",
      "messages":      [],
      "details": {
        "numero_comprobantes": 1
      }
    },
    "sri": {
      "access_key": "0605202601179000000100110010010000001231234567812",
      "version":    "1.1.0",
      "ambiente":   "2"
    }
  },
  "checks": {
    "format":     true,
    "signature":  true,
    "tax_status": true
  }
}

Probar este endpoint

Envía un XML real desde tu navegador. La key se usa sólo para este request — no se guarda en el servidor.

Adjunta un XML y pega tu API key.
# adjunta un XML para generar el curl
en espera
La respuesta aparecerá aquí.

El objeto validation

CampoTipoDescripción
objectstringSiempre "validation".
request_idstringIdentificador de la solicitud.
countrystringec, pe.
validbooleantrue sólo si pasan todas las verificaciones solicitadas.
validated_atstringISO 8601 UTC.
documentobjectForma unificada EC + PE. Detalle abajo.
checksobjectResumen booleano. Catálogo.
failureobjectSólo si valid: false. {stage, code, message}. Catálogo.
errorsarray{source, code, message, path?}.
warningsarrayNo afectan valid. Misma forma que errors.

El objeto document — campo por campo

Campos vacíos se omiten. La columna Origen marca aplicabilidad EC / PE.

Identificadores del documento

CampoTipoOrigenDescripción
typestringEC, PETipo, sin prefijo de país. Ver catálogo.
serialstringEC, PEEC: estab-ptoEmi-secuencial (ej. 001-001-000000123). PE: cbc:ID tal cual (ej. F001-001).
issue_datestringEC, PEEC: dd/mm/yyyy (formato SRI). PE: yyyy-mm-dd (ISO 8601).
environmentstring, nullEC, PE"live", "test", null. Ver catálogo.

document.issuerEmisor

CampoTipoDescripción
issuer.idstringRUC del emisor. EC: 13 dígitos. PE: 11 dígitos.
issuer.namestringRazón social del emisor.

document.signatureFirma electrónica

Presente sólo si el documento lleva firma XAdES.

CampoTipoDescripción
signature.signed_atstringFecha de la firma, ISO 8601 con offset.
signature.signerstringCommon Name del certificado firmante.
signature.certificateobjectDetalle del certificado X.509 usado para firmar.

document.signature.certificate

CampoTipoDescripción
certificate.serialstringSerial hex con separadores : (ej. 01:23:45:67).
certificate.subjectobjectDN del titular. Ver desglose abajo.
certificate.issuerobjectDN de la AC emisora. Mismo desglose que subject.
certificate.not_beforestringInicio de validez (ISO 8601 UTC).
certificate.not_afterstringFin de validez (ISO 8601 UTC).
certificate.signature_algorithmstringAlgoritmo de firma (ej. "SHA-256 with RSA").
certificate.key_usagearray<string>Usos permitidos ("Digital Signature", "Non Repudiation", …).
certificate.extended_key_usagearray<string>EKU declarados.
certificate.subject_alt_namesarray<string>SAN (DNS, email, URI…) prefijado con su tipo.
certificate.crl_urlsarray<string>Puntos de distribución de CRL.
certificate.ocsp_urlsarray<string>Endpoints OCSP del emisor.
certificate.policiesarray<string>OIDs de políticas de certificación.
certificate.thumbprint_sha256stringHuella SHA-256 del certificado, hex con :.

certificate.subject y certificate.issuerDistinguished Name

CampoTipoDescripción
common_name (CN)stringNombre común del titular o de la AC.
id_numberstringCédula, DNI, RUC del titular (extraído de OID 2.5.4.45 o serialNumber).
organization (O)stringOrganización titular del certificado.
organizational_unit (OU)stringUnidad organizacional.
country (C)stringPaís, código ISO de 2 letras.
state (ST)stringProvincia, departamento.
locality (L)stringCiudad.
emailstringEmail asociado al titular (si está declarado).
rawstringDN crudo, formato RFC 2253.

document.tax_statusEstado fiscal

Presente sólo si se incluyó tax_status en checks.

CampoTipoOrigenDescripción
tax_status.authoritystringEC, PE"sri", "sunat".
tax_status.statusobjectEC, PE{accepted, code, label}. Códigos en catálogo.
tax_status.status.acceptedboolean, nullEC, PEtrue sólo si la autoridad lo autorizó. null si la consulta falló.
tax_status.status.codestringEC, PECódigo literal de la autoridad (ej. AUTORIZADO, ACEPTADO).
tax_status.status.labelstringEC, PEEtiqueta legible en español.
tax_status.authorized_atstringECFecha de autorización SRI (ISO 8601 con offset). SUNAT no la expone.
tax_status.numberstringECNúmero de autorización del SRI (= claveAcceso en e-fact 2.x).
tax_status.environmentstringECEntorno reportado por SRI: "live", "test".
tax_status.messagesarray<object>EC, PEAvisos de la autoridad: {id, type, text, details}.
tax_status.detailsobjectEC, PEDatos crudos. PE: estado_cp, estado_ruc, cond_domicilio, etc. EC: numero_comprobantes.
tax_status.errorstringEC, PEMotivo si la consulta no se pudo completar (timeout, credenciales, RUC sin enrolar).

document.sriSólo Ecuador

CampoTipoDescripción
sri.access_keystringclaveAcceso de 49 dígitos.
sri.versionstringVersión del esquema XSD (atributo version de la raíz).
sri.ambientestring"1" (pruebas), "2" (producción), tal cual viene en el XML.

document.sunatSólo Perú

CampoTipoDescripción
sunat.ubl_versionstringcbc:UBLVersionID (ej. "2.1").
sunat.customizationstringcbc:CustomizationID (ej. "2.0").
SUNAT requiere credenciales propias. Regístralas en SOL → Empresas → Credenciales de API SUNAT y entréganos client_id, client_secret y el RUC bajo el que registraste la app. Sin credenciales, tax_status.accepted = null con motivo en tax_status.error; el resto de checks no se ve afectado.
Documento Perú — SUNATJSON
{
  "object": "validation",
  "request_id": "req_01HXY2K9P3M7T8B",
  "country": "pe",
  "valid": true,
  "validated_at": "2026-05-06T14:23:11Z",
  "document": {
    "type":       "invoice",
    "serial":     "F001-001",
    "issue_date": "2026-04-01",
    "issuer": {
      "id":   "20100000001",
      "name": "EMPRESA DEMO PERU S.A.C."
    },
    "signature": {
      "signed_at":   "2026-05-05T08:14:02Z",
      "signer":      "MARIA RODRIGUEZ EJEMPLO",
      "certificate": {
        "serial":              "0A:0B:0C:0D",
        "subject": {
          "common_name":         "EMPRESA DEMO PERU S.A.C.",
          "id_number":           "20100000001",
          "organization":        "EMPRESA DEMO PERU S.A.C.",
          "organizational_unit": "FACTURACION ELECTRONICA",
          "country":             "PE",
          "locality":            "LIMA"
        },
        "issuer": {
          "common_name":         "AC DEMO PE",
          "organization":        "DEMO CERTIFICADORA PE S.A.C.",
          "organizational_unit": "AUTORIDAD DE CERTIFICACION",
          "country":             "PE",
          "locality":            "LIMA"
        },
        "not_before":          "2025-09-12T15:21:08Z",
        "not_after":           "2027-09-12T15:21:08Z",
        "signature_algorithm": "SHA-256 with RSA",
        "key_usage":           ["Digital Signature", "Non Repudiation"],
        "subject_alt_names":   ["Email: firmas@empresa-demo.example.pe"],
        "crl_urls":            ["http://crl.example.pe/demo.crl"],
        "ocsp_urls":           ["http://ocsp.example.pe"],
        "policies":            ["2.16.604.1.99999.1"],
        "thumbprint_sha256":   "AA:BB:CC:DD:EE:FF:00:11:..."
      }
    },
    "tax_status": {
      "authority": "sunat",
      "status": {
        "accepted": true,
        "code":     "ACEPTADO",
        "label":    "Aceptado"
      },
      "messages": [],
      "details": {
        "estado_cp":           "1",
        "estado_ruc":          "00",
        "estado_ruc_label":    "Activo",
        "cond_domicilio":      "00",
        "cond_domicilio_label": "Habido"
      }
    },
    "sunat": {
      "ubl_version":   "2.1",
      "customization": "2.0"
    }
  },
  "checks": {
    "format":     true,
    "signature":  true,
    "tax_status": true
  }
}

Errores

Dos formatos: errores de la solicitud (autenticación, formato) devuelven error; documentos inválidos devuelven validation con valid: false y un campo failure.

Errores de la solicitud (4xx)

CampoTipoDescripción
typestringFamilia: authentication_error, invalid_request_error, api_error.
codestringCódigo específico, estable y legible por máquina.
messagestringDescripción legible para humanos, en español.
request_idstringIdentificador de la solicitud para soporte.

Documentos inválidos (422)

failure resume la causa primaria; checks y errors tienen el detalle.

failure.stagefailure.codeSignificado
parsexml_parse_errorEl XML está mal formado o no es UTF-8 válido.
detectunknown_document_typeNo se pudo identificar país, tipo o versión del documento.
schemaschema_invalidEl XML no cumple con el XSD oficial del SRI o SUNAT.
signaturesignature_invalidLa firma electrónica no verifica (digest no coincide).
signaturesignature_untrustedLa cadena de certificación no es de confianza.
signaturesignature_expiredEl certificado del firmante está vencido o aún no es válido.
rulesbusiness_rules_failedEl documento no cumple con las reglas de negocio del SRI/SUNAT.
Error de la solicitud400
{
  "error": {
    "type":       "invalid_request_error",
    "code":       "invalid_country",
    "message":    "País no soportado: \"br\". Valores aceptados: 'ec', 'pe'.",
    "request_id": "req_01HXY2K9P3M7T8B"
  }
}
Documento inválido422
{
  "object": "validation",
  "valid": false,
  "request_id": "req_01HXY2K9P3M7T8B",
  "country": "ec",
  "failure": {
    "stage":   "signature",
    "code":    "signature_invalid",
    "message": "La firma electrónica no es válida (los datos firmados no coinciden con el resumen)."
  },
  "document": {
    "type": "invoice",
    "signature": {
      "signer": "JUAN PEREZ EJEMPLO"
    },
    "sri": {
      "version": "1.1.0"
    }
  },
  "checks": {
    "format":    true,
    "signature": false
  },
  "errors": [
    {
      "source":  "signature",
      "code":    "INTEGRITY",
      "message": "signature integrity check failed (digest mismatch or untrusted signer)"
    }
  ],
  "warnings": [],
  "validated_at": "2026-05-06T14:23:11Z"
}

Códigos HTTP

CódigoSignificado
200La solicitud fue procesada. Revisa valid para conocer el resultado.
400El cuerpo de la solicitud está malformado.
401El API key falta o es inválido.
403El API key no tiene permiso sobre el recurso o país solicitado.
422El cuerpo es válido pero los datos no superan la validación.
429Demasiadas solicitudes. Espera y reintenta con backoff.
500Error interno. Contáctanos con el request_id.
503Los servicios del SRI o SUNAT no están disponibles temporalmente.
Reintentos429, 500, 503
# Backoff exponencial con jitter.
# No reintentar 4xx (excepto 429).

Límites

  • Tamaño máximo del XML: 5 MB.
  • Latencia típica: < 1 s (formato + firma); < 3 s con consulta SRI/SUNAT.

Planes

PlanPrecioIncluye
MensualUSD 299 / mesConsultas ilimitadas, EC + PE.
AnualUSD 2,999 / añoConsultas ilimitadas + 2 meses gratis.

Precios sin impuestos.

Cabeceras informativasHTTP
X-Request-Id: req_01HXY2K9P3M7T8B
X-Response-Time: 412ms

Cumplimiento

Norma — AcreditaciónReferencia
ISO/IEC 27001:2022 — Sistema de Gestión de Seguridad de la Información (SGSI) sobre los sistemas que soportan el proceso de facturación electrónica. AENOR, IQNET, Reg. ES-SI-0062/2021, vigente hasta 2027-09-20.
ARCOTEL — Entidad de Certificación de Información acreditada (Ecuador). Resolución ARCOTEL-2021-0923.
SUNAT — Proveedor de Servicios Electrónicos acreditado (Perú). Resolución 034-005-0012054/SUNAT.
SRI — Proveedor reconocido de facturación electrónica (Ecuador). Servicio de Rentas Internas.

Soporte

Si nedesitas soporte, contáctanos por los siguientes canales oficiales:

Estado del servicio
# conectando…

Code reference

La documentación interna del código tax-api se publica automáticamente desde master. Útil para clientes que ejecutan tax-api en su propia infraestructura (self-hosted) y para auditorías de implementación.

datil.github.io/datil/tax-api ↗

La superficie HTTP descrita arriba es la fuente de verdad para integraciones; los módulos internos pueden cambiar entre versiones sin previo aviso.

Qué encontrarás
# Módulos públicos
TaxApi.TaxDocs       # pipeline de validación
TaxApi.SRI           # cliente catastro Ecuador
TaxApi.SUNAT         # cliente validarcomprobante
TaxApi.Tenants       # cuentas y API keys

# Generado con
mix docs