Saltar al contenido

Introducción

RAVEN DTE · SaaS

API REST y webhooks

Referencia para emitir, firmar y transmitir documentos tributarios electrónicos desde el sistema de la empresa, y para recibir el resultado por webhook.

Actualizado el 23 de octubre de 2026

Introducción

RAVEN DTE emite el documento, lo firma con JWS RS512 y lo transmite al Ministerio de Hacienda de El Salvador bajo la Normativa 2.0. Esta página cubre la superficie de integración de la empresa: facturación, compras, sucursales, reportes y webhooks. Quedan fuera las rutas internas de operación de la plataforma.

Los ejemplos usan personas y sellos ficticios. <API_KEY>, <ACCESS_TOKEN>, <WEBHOOK_SECRET> y <UUID_V4> son marcadores: reemplazalos con los valores de tu cuenta. No pegues una clave real en un repositorio.

OpenAPI 3.0 se sirve en el host de la API, no en este sitio: https://api.raventech.com.sv/docs y https://api.raventech.com.sv/openapi.json. También responden /api/docs y /api/openapi.json.

Entornos y URL base

La base canónica es https://api.raventech.com.sv/v1. El inventario nombra las rutas con el prefijo /api/v1, que el servidor sigue aceptando. En la URL canónica no se repite /api.

Cómo armar la URL de emisión
FormaEjemplo
CanónicaPOST https://api.raventech.com.sv/v1/dtes
Alias /api/v1POST https://api.raventech.com.sv/api/v1/dtes
Alias del routerPOST https://api.raventech.com.sv/v1/dte

Usá HTTPS en cuanto haya credenciales, claves o datos fiscales. El prefijo /v1 es de esta API. Las direcciones del Ministerio de Hacienda son otro servicio.

Ambientes fiscales
CódigoUso
00Sandbox. Pruebas de integración, sin validez legal.
01Producción. El sello tiene efecto tributario.

Rutas de servicio

Declaradas junto a la API, fuera de las 249 rutas de negocio.

Rutas de servicio del host de la API
MétodoRutaPara qué
GET/health y /v1/healthEstado del servicio.
GET/docs y /openapi.jsonOpenAPI interactivo y JSON. Alias: /api/docs y /api/openapi.json.
GET/api/v1/public/founder-spotsCupos públicos de la promoción de fundadores. La variante autenticada está en promociones.

Guía rápida

  1. Obtené una API key (POST /api/v1/apikeys, Plan Pro) o un token con POST /api/v1/auth/login.
  2. Enviá POST /api/v1/dtes con tipoDte, receptor, ítems y X-Idempotency-Key.
  3. Leé estado, selloRecibido y urls.
  4. Descargá el JSON o el PDF público si el documento fue aceptado.
  5. Registrá un webhook para dte.accepted y verificá X-Webhook-Signature.

Autenticación

Las rutas protegidas aceptan una de estas cabeceras:

Cabeceras de autenticación
CabeceraCuándo usarla
X-API-Key: <API_KEY>Integración de sistema. La clave representa a la empresa y trae sus propios permisos. Formato documentado: raven_live_… o raven_test_…
Authorization: Bearer <ACCESS_TOKEN>Sesión de una persona. El token sale de POST /api/v1/auth/login.

Pasar la autenticación no autoriza todas las operaciones. Algunas rutas exigen rol, permiso, sucursal o plan. En el contexto de un contador verificado, X-Accountant-Tenant-Id consulta una empresa autorizada; ese middleware no habilita la emisión.

La emisión en POST /api/v1/dtes usa middleware de idempotencia. Enviá X-Idempotency-Key con un UUID v4 para que un reintento de red no timbre dos veces. Cabeceras opcionales de la emisión: X-Branch-Id y X-Pos-Id, cada una con un UUID, si la operación corresponde a una sucursal o a una caja.

El ciclo de sesión está en el catálogo de autenticación: registro, login, verificación MFA, refresh, verificación de correo, olvido y cambio de contraseña, perfil, 2FA, exportación y desactivación de cuenta, y cierre de sesión.

Solicitud de asesoría

POST /api/v1/auth/contact-request es público y tiene límite de tasa. Sirve para dejar un prospecto desde un formulario.

Cuerpo de la solicitud de asesoría
CampoRegla
nombreTexto, opcional.
empresaTexto, requerido.
emailTexto, requerido.
telefonoTexto, opcional.
origenTexto. Si no se envía, el valor es login_modal.

La respuesta 201 trae status, lead, message y whatsappUrl. El equipo comercial recibe el aviso en info@raventech.com.sv.

Errores y límites

La respuesta de error suele incluir error y code. No todos los módulos comparten el mismo esquema.

Códigos HTTP habituales
CódigoSignificado
400Validación del cuerpo o del esquema.
401Credencial ausente o inválida.
403Autenticado, sin permiso para la operación.
404El recurso no existe o no es de esta empresa.
429Límite de tasa.
Límites de tasa documentados
SuperficieLímiteRespaldo
Descargas públicas /json, /pdf y /ticket10 peticiones por minuto, por IPVerificado en la guía, con cabeceras RateLimit.
Rutas autenticadas generales100 peticiones por minuto, por usuarioIndicado en la especificación del portal.
API keysHasta 500 peticiones por hora, ajustable por planIndicado en la especificación del portal.

Tipos de DTE

OpenAPI refleja estos 11 tipos. El cuerpo válido de uno no sirve para otro: tipoDte, receptor e ítems tienen que cumplir la validación del servicio y el esquema de Hacienda que corresponda.

Códigos tipoDte
CódigoDocumento
01Factura electrónica, consumidor final.
03Comprobante de crédito fiscal.
04Nota de remisión.
05Nota de crédito electrónica.
06Nota de débito electrónica.
07Comprobante de retención electrónico.
08Comprobante de liquidación.
09Documento contable de liquidación.
11Factura de exportación.
14Factura de sujeto excluido.
15Comprobante de donación.

Departamentos, municipios, actividades y tributos salen de GET /api/v1/catalogs/{type}.

Emitir un DTE

POST /api/v1/dtes. Alias equivalente: POST /api/v1/dte. Autenticación con API key o Bearer. Cabeceras: Content-Type: application/json, X-Idempotency-Key recomendada, y de forma opcional X-Branch-Id y X-Pos-Id.

cURL

curl -X POST "https://api.raventech.com.sv/v1/dtes" \
  -H "X-API-Key: <API_KEY>" \
  -H "Content-Type: application/json" \
  -H "X-Idempotency-Key: <UUID_V4>" \
  -d '{
  "tipoDte": "01",
  "receptor": {
    "tipoDocumento": "13",
    "numDocumento": "01234567-8",
    "nrc": null,
    "nombre": "JUAN PÉREZ GARCÍA",
    "codActividad": null,
    "descActividad": null,
    "direccion": {
      "departamento": "05",
      "municipio": "23",
      "complemento": "Colonia Escalón, Calle 2, San Salvador"
    },
    "telefono": "78901234",
    "correo": "juan.perez@example.com"
  },
  "cuerpoDocumento": [
    {
      "numItem": 1,
      "tipoItem": 1,
      "cantidad": 2,
      "codigo": "SERV-001",
      "descripcion": "Desarrollo y Consultoría de Software",
      "precioUni": 50.0,
      "montoDescu": 0.0,
      "ventaNoSuj": 0.0,
      "ventaExenta": 0.0,
      "ventaGravada": 100.0,
      "tributos": null
    }
  ],
  "extension": null,
  "apendice": null
}'

Respuesta ilustrativa de éxito (201 o 200):

JSON

{
  "success": true,
  "estado": "ACCEPTED",
  "codigoGeneracion": "6C8D7614-2D99-4A1B-87A3-20F3E6BA109B",
  "numeroControl": "DTE-01-M001P001-000000000000042",
  "selloRecibido": "2026B1E63C4040F595305141011A8DFD0C475D0D",
  "fhProcesamiento": "2026-10-18T10:15:30-06:00",
  "clasificaMsg": "10",
  "descripcionMsg": "RECIBIDO Y PROCESADO SATISFACTORIAMENTE",
  "urls": {
    "json": "https://api.raventech.com.sv/v1/dtes/6C8D7614-2D99-4A1B-87A3-20F3E6BA109B/json",
    "pdf": "https://api.raventech.com.sv/v1/dtes/6C8D7614-2D99-4A1B-87A3-20F3E6BA109B/pdf",
    "ticket": "https://api.raventech.com.sv/v1/dtes/6C8D7614-2D99-4A1B-87A3-20F3E6BA109B/ticket"
  }
}

Respuesta ilustrativa de validación (400):

JSON

{
  "success": false,
  "error": "Error de validación contra esquema DGII",
  "code": "SCHEMA_VALIDATION_ERROR",
  "detalles": [
    {
      "campo": "receptor.numDocumento",
      "mensaje": "El número de documento no cumple con el formato oficial para el tipo 13 (DUI)"
    }
  ]
}

Un borrador guardado responde 201 con success, estado: PENDING y codigoGeneracion, sin correlativo definitivo ni transmisión.

Borradores y transmisión

Ciclo del DTE antes y después del sello
AcciónRutaRegla
Guardar borradorPOST /api/v1/dtes/borradorQueda en PENDING. Sirve para pausar una venta y retomarla.
TransmitirPOST /api/v1/dtes/{codigoGeneracion}/transmitirAsigna correlativo, firma en JWS RS512 y transmite un DTE que está en PENDING.
DescartarDELETE /api/v1/dtes/{codigoGeneracion}Solo un PENDING. Un ACCEPTED ya sellado se liquida con el evento de invalidación.

Las mismas rutas existen bajo /api/v1/dte. El listado completo está en DTE y alias /dte.

Consultar y listar

GET /api/v1/dtes/{codigoGeneracion} acepta el UUID, el número de control o el ID numérico. El listado es GET /api/v1/dtes.

Filtros del listado de DTE
ParámetroUso
tipoDteCódigo de dos caracteres, por ejemplo 01 o 03.
estadoACCEPTED, REJECTED, CANCELLED, CONTINGENCY o PENDING.
fechaDesde, fechaHastaRango de emisión, formato YYYY-MM-DD.
searchTexto por cliente, NRC o número de control.
branch_id, pos_idSucursal o caja.
page, limitPaginación. El ejemplo de la guía usa page=1 y limit=20.

Ejemplo: /api/v1/dtes?tipoDte=01&estado=ACCEPTED&fechaDesde=2026-10-01&fechaHasta=2026-10-31&page=1&limit=20

Notas y comprobantes comerciales

Rutas dedicadas, además de la emisión genérica:

  • POST /api/v1/dtes/nota-credito y nota-debito
  • POST /api/v1/dtes/comprobante-retencion y factura-exportacion
  • GET /api/v1/dtes/sugerencia-dcl suma las ventas aceptadas del período emitidas por cuenta del afiliado.

Los comprobantes comerciales (COM-01, COM-03, COM-11) son emisión no tributaria para una empresa sin certificado activo en Hacienda. Tienen PDF privado y una conversión transaccional a DTE fiscal con la fecha actual, en /api/v1/commercial-documents. Un reintento de esa conversión reutiliza el mismo intento fiscal firmado.

Descargas públicas

Pensadas para el QR del documento o el correo del receptor. No llevan API key. El límite verificado es 10 peticiones por minuto por IP, con cabeceras RateLimit y Content-Disposition.

Descargas públicas del DTE
RutaRespuesta
GET /api/v1/dtes/{codigoGeneracion}/jsonJSON sellado y firmado con JWS RS512.
GET …/pdfPDF tamaño carta. El inventario documenta ?format=ticket o carta. La especificación también documenta ?download=true para forzar la descarga.
GET …/ticketTirilla térmica de 80 mm o 58 mm.
GET …/ticket-printHTML que dispara la impresión sin abrir otra pestaña.

cURL

curl -L "https://api.raventech.com.sv/v1/dtes/<CODIGO_GENERACION>/pdf" -o dte.pdf

Eventos fiscales

La invalidación es POST /api/v1/dtes/anular. El plazo es el del Código Tributario según el tipo de DTE. Ejemplo de cuerpo:

JSON

{
  "codigoGeneracion": "6C8D7614-2D99-4A1B-87A3-20F3E6BA109B",
  "motivo": {
    "tipoAnulacion": 2,
    "motivoAnulacion": "Rescisión total de la operación comercial por acuerdo entre las partes",
    "nombreResponsable": "CARLOS MENDOZA",
    "tipDocResponsable": "13",
    "numDocResponsable": "02345678-9",
    "nombreSolicita": "JUAN PÉREZ GARCÍA",
    "tipDocSolicita": "13",
    "numDocSolicita": "01234567-8"
  }
}
Eventos y sus descargas públicas
EventoTransmisiónDescarga pública
InvalidaciónPOST /api/v1/dtes/anularGET …/invalidacion/json y …/invalidacion/pdf
Retorno de bienes, tipo 18POST /api/v1/dtes/eventos/retornoGET …/retorno/json y …/retorno/pdf. Aplica a documentos 01, 11 o 14.
Operaciones especiales, tipo 17POST /api/v1/dtes/eventos/operaciones-especialesGET …/operaciones-especiales/json y …/operaciones-especiales/pdf

GET /api/v1/dtes/events/{codigoGeneracion}/pdf devuelve la carta del evento 17 o 18 según lo almacenado. El historial auditable es GET /api/v1/dtes/eventos/historial.

Contingencia

Cuando no hay internet o Hacienda no responde, los comprobantes quedan pendientes de lote. POST /api/v1/contingency/transmit-batch arma el lote: máximo 50 comprobantes. GET /api/v1/contingency/pending lista lo pendiente, GET /api/v1/contingency/events el historial y GET /api/v1/contingency/stats las cifras de la empresa. Tabla en contingencia.

Webhooks

Todas estas rutas exigen autenticación y quedan filtradas por empresa. Si omitís secret, el servidor genera 32 bytes aleatorios en hexadecimal y te lo devuelve. Guardalo: la consulta también lo incluye. headers se agregan a la petición saliente, pero el servicio descarta las cabeceras reservadas y conserva las de firma e identificación. La URL debe apuntar a un destino público; acepta http o https, aunque conviene usar HTTPS.

HTTP

POST /api/v1/webhooks HTTP/1.1
Authorization: Bearer <ACCESS_TOKEN>
Content-Type: application/json

{
  "url": "https://cliente.example/webhooks/raven",
  "eventos": ["dte.accepted", "dte.rejected"],
  "secret": "<WEBHOOK_SECRET>"
}
Administración de webhooks
MétodoRutaResultado
POST/api/v1/webhooksRegistra url y eventos. secret y headers son opcionales. Responde 201.
GET/api/v1/webhooksDevuelve { webhooks: [...] }.
GET/api/v1/webhooks/eventosDevuelve { eventos: [...] }.
GET/api/v1/webhooks/{id}404 si no existe o es de otra empresa.
PUT/api/v1/webhooks/{id}Campos opcionales: url, eventos, secret, headers, activo.
DELETE/api/v1/webhooks/{id}Elimina la suscripción.
GET/api/v1/webhooks/{id}/logs?limit=50Historial. limit es opcional y la ruta no le pone tope.
POST/api/v1/webhooks/{id}/testEncola un evento test.
POST/api/v1/webhooks/{id}/deliveries/{jobId}/replayReencola una entrega fallida con el mismo id y timestamp.

Eventos de webhook

GET /api/v1/webhooks/eventos publica los eventos que se pueden registrar. Eso no significa que todos se disparen hoy.

Eventos registrables y disparos observados
EventoDescripciónDisparo observado
dte.createdDTE creado.Sí.
dte.acceptedDTE aceptado por Hacienda.Sí.
dte.rejectedDTE rechazado.Sí.
dte.cancelledDTE invalidado o cancelado, según el flujo que lo emita.No conectado en la revisión.
certificate.uploadedCertificado cargado.No conectado en la revisión.
certificate.expiredCertificado vencido.No conectado en la revisión.
contingency.activatedContingencia activada.No conectado en la revisión.
contingency.deactivatedContingencia desactivada.No conectado en la revisión.
dte.contingencyNombre usado por el disparo de contingencia.Sí; también es registrable.
testLo envía POST …/test.Sí, sin estar en la lista.

Ejemplo ilustrativo de dte.accepted. El contrato mínimo es id, evento, timestamp y data. El id identifica el evento para deduplicar entregas.

JSON

{
    "id": "c5efed03-0dc5-4ba5-81f2-080543b539d4",
  "evento": "dte.accepted",
  "timestamp": "2026-10-18T10:15:32.450Z",
  "data": {
    "codigoGeneracion": "6C8D7614-2D99-4A1B-87A3-20F3E6BA109B",
    "numeroControl": "DTE-01-M001P001-000000000000042",
    "selloRecibido": "2026B1E63C4040F595305141011A8DFD0C475D0D",
    "tipoDte": "01",
    "totalPagar": 100.0,
    "receptor": {
      "nombre": "JUAN PÉREZ GARCÍA",
      "numDocumento": "01234567-8"
    },
    "urls": {
      "pdf": "https://api.raventech.com.sv/v1/dtes/6C8D7614-2D99-4A1B-87A3-20F3E6BA109B/pdf",
      "json": "https://api.raventech.com.sv/v1/dtes/6C8D7614-2D99-4A1B-87A3-20F3E6BA109B/json"
    }
  }
}

Cabeceras de cada entrega:

  • Content-Type: application/json
  • User-Agent: FEL-Webhook/1.0
  • X-Webhook-Event con el nombre del evento
  • X-Webhook-Id con el mismo id del cuerpo
  • X-Webhook-Signature con 64 caracteres hexadecimales

Firma HMAC-SHA256

X-Webhook-Signature es el hexadecimal de HMAC-SHA256 sobre los bytes de JSON.stringify de { id, evento, timestamp, data }, con el secreto compartido. Verificá el cuerpo crudo, sin reordenar propiedades ni volver a serializar.

Node.js

const crypto = require("crypto");
const express = require("express");
const app = express();

function validarWebhook(rawBody, signature, secret) {
  if (!/^[0-9a-f]{64}$/i.test(signature || "")) return false;
  const expected = crypto.createHmac("sha256", secret).update(rawBody).digest();
  return crypto.timingSafeEqual(expected, Buffer.from(signature, "hex"));
}

// Registrar esta ruta antes de app.use(express.json()).
app.post("/mi-webhook-dte", express.raw({ type: "application/json" }), (req, res) => {
  const firma = req.headers["x-webhook-signature"];
  const ok = validarWebhook(req.body, firma, process.env.RAVEN_WEBHOOK_SECRET);
  if (!ok) return res.sendStatus(401);

  const payload = JSON.parse(req.body.toString("utf8"));
  // Procesar payload.evento de forma idempotente.
  return res.sendStatus(200);
});

El ejemplo de Node compara el digest en binario y acepta el hex en mayúsculas o minúsculas. PHP y Python, tal como están arriba, comparan el texto hexadecimal: la diferencia de mayúsculas no coincide.

API keys, usuarios y roles

POST /api/v1/apikeys está habilitado en Plan Pro y devuelve la clave en claro una sola vez. GET lista las claves enmascaradas. DELETE /api/v1/apikeys/{id} revoca de inmediato.

POST /api/v1/users, PUT /api/v1/users/{id} y el listado aceptan chg_password para obligar el cambio de clave en el próximo inicio de sesión. El alta, la edición, el estado y la baja lógica están en usuarios. Los roles y el catálogo de permisos están en roles.

Libros de IVA

Libros de compras y ventas, anexos F-07 v14 y F-14, y cierre mensual con una foto inmutable. El CSV del anexo va delimitado por punto y coma y codificado en windows-1252, listo para la carga en Hacienda. El período se escribe YYYY-MM. Reabrir un período cerrado pide justificación y queda en auditoría. Rutas en libros de IVA, incluida la compra extemporánea.

Compras e inventario

POST /api/v1/purchases/import recibe el JSON de un DTE de compra. Desde ahí se puede sectorizar la compra para el F-07 v14, emitir el comprobante de retención del 1% (CRE 07) y cargar el kardex. La retención de renta del art. 156 del Código Tributario es un registro aparte, independiente del CRE 07, con anulación y PDF. El detalle está en compras y el catálogo de productos en productos e inventario.

Sucursales, cajas y turnos

Establecimientos y puntos de emisión, con casa matriz, stock por sucursal, transferencias y bloqueo de emisión por empresa, sucursal o caja. Los turnos cubren apertura, turno activo, resumen para el arqueo, cierre y el ticket térmico de corte en 58 mm o 80 mm. Tablas: sucursales y turnos de caja.

Cotizaciones y facturación recurrente

Una cotización se crea, se edita, se manda por correo y se descarga en PDF. PATCH /api/v1/quotes/{id}/status cambia el estado (aceptar, rechazar o facturar). La facturación recurrente registra el programa, lo pausa, dispara una emisión inmediata o lo cancela. Rutas en cotizaciones y facturación recurrente.

Cuentas por cobrar

Cartera de documentos a crédito, abonos, reversa del abono y ticket térmico del pago. POST /api/v1/accounts-receivable/sync incorpora DTE a crédito que hayan quedado pendientes. Tabla en cuentas por cobrar.

Portal del contador

El contador consulta su panel multiempresa, actualiza el perfil con inscripción y declaración, y acepta o rechaza invitaciones. La empresa invita, ajusta alcances y puede revocar el acceso con efecto inmediato. El comprobante interno no fiscal usa la serie propia del contador (la especificación de producto indica CI-000001 a CI-000100); se anula sin perder el correlativo y se imprime en carta. La descarga masiva pide un ZIP en segundo plano. Todo el módulo está en portal del contador.

Certificación en sandbox

En ambiente 00 se consulta el avance, se inicia una corrida de los 11 tipos de DTE y los 4 eventos, y esa corrida se puede pausar, reanudar o detener. GET /api/v1/certification/evidence/zip baja el paquete con los JSON sellados y el manifiesto SHA-256. Rutas en certificación.

Reportes

GET /api/v1/reports/tenant calcula sobre el universo filtrado, no sobre una página truncada. Códigos: ventas T-01, DTE transmitidos T-02, anulaciones T-03, compras T-04, cuentas por cobrar T-05, inventario T-06 y turnos T-07. La paginación informa total_records y has_more.

Filtros de reportes de la empresa
ParámetroUso
codesUno o varios códigos T-01 a T-07.
fecha_desde, fecha_hastaRango del reporte.
branch_id, pos_idSucursal o caja.
ambiente00 o 01.
limit, pagePaginación del JSON.

GET /api/v1/reports/export entrega Excel (format=xlsx, una hoja por reporte) o PDF apaisado (format=pdf). Usá codes o single_code. El tope documentado del archivo es 10 000 registros, con totales agregados.

Modo simulación

La demo corre aislada y no escribe la operación fiscal. POST /api/v1/simulation/start abre la sesión con nombre, correo obligatorio y teléfono opcional, y devuelve un JWT temporal. POST /api/v1/simulation/end la cierra y calcula la duración. El mismo módulo puede renderizar PDF de demostración y exponer metadatos comerciales públicos, sin sello fiscal. Rutas en simulación.

Planes y promociones

La empresa consulta el plan, la cuota, la configuración del programa y los planes disponibles. Puede subir de NEST a WING y de WING a FLIGHT, pedir el enlace de pago, activar DTE adicionales o pasar a modo solo consulta. El adicional documentado es $0.03 + IVA por DTE, y $0.0255 + IVA cuando lo activa el contador.

El perfil de facturación permite consultar o actualizar el comprobante preferido, los datos fiscales y el cobro automático. El historial de facturas recibidas incluye enlaces a los PDF y JSON oficiales. Estas tres rutas están en el catálogo de planes.

Las promociones validan un código, lo aplican antes del primer pago confirmado y muestran el panel «Recomendá RAVEN DTE» y «Mi descuento». Los cupos públicos de fundadores también están en GET /api/v1/promotions/public/founder-spots. Tablas: planes y promociones.

Empresa, clientes y catálogos

La empresa lee y actualiza su ficha, el logo, el certificado, las credenciales de la API de Hacienda y las 11 secuencias por ambiente y año. El checklist de pase de ambiente 00 a 01 está en las rutas de production-readiness y production-transition. Hay vista previa del PDF carta.

Clientes, certificados de la cuenta (el activo se consulta sin las llaves) y catálogos oficiales están en empresa, clientes, certificados y catálogos de Hacienda.

Correo y notificaciones

La plantilla de correo de la empresa se consulta, se edita y se previsualiza con datos de ejemplo. Se puede reenviar el DTE a una dirección y ver el historial de envíos. Las notificaciones de la persona autenticada se listan, se marcan una a una o todas. Tablas: correo y notificaciones.