Ir al contenido

WebhooksCatálogo de eventos

Catálogo de eventos

Ver como Markdown

Toda entrega llega con la misma forma exterior, sin importar el evento:

{
"id": "evt_018f3c2a1e6b7d40a5c8e1d2b3a4f506",
"object": "event",
"type": "ecf.accepted",
"apiVersion": "1",
"createdAt": "2026-09-07T14:03:11-04:00",
"data": {
"object": { "...": "el recurso afectado" }
}
}
Campo Nota
id Único por evento. Es tu clave para deduplicar (ver Verificar firma).
object Siempre "event".
type El tipo de evento, por ejemplo ecf.accepted.
apiVersion Versión del formato del sobre, hoy "1".
createdAt Hora dominicana, como todo lo que emite la API.
data.object El recurso afectado. Salvo que se indique lo contrario abajo, tiene la misma forma que el GET correspondiente.

Te suscribes a un tipo exacto, a una categoría completa con comodín (ecf.* cubre los 6 del ciclo de vida más ecf.duplicate_suspected), o a todo con *.

Evento Cuándo se dispara
ecf.submitted La DGII recibió el comprobante y devolvió un TrackId. Se repite si NovaFE reenvía el comprobante.
ecf.accepted La DGII lo aceptó: fiscalmente válido.
ecf.accepted_conditional Aceptado con una observación, igualmente válido.
ecf.rejected La DGII lo rechazó de forma definitiva.
ecf.review La DGII no resolvió tras el ladder de consultas.
ecf.failed Falló el transporte tras agotar los reintentos.

ecf.rejected solo se emite cuando el rechazo es definitivo. Si la DGII falla de forma pasajera y NovaFE reenvía el comprobante por su cuenta, no se emite mientras dure el reintento; se emite únicamente si los reintentos se agotan. Ver Rechazos y reintentos.

data.object es el mismo EcfDto que devuelve GET /ecf/{id}. No existe un evento ecf.signed: ese estado ya lo tienes en la respuesta de POST /ecf. Si tu cuenta no espera el resultado de la DGII al emitir, el primer evento que recibirás de cada comprobante es ecf.submitted.

Evento Cuándo se dispara
ecf.duplicate_suspected Con el modo observar de detección de duplicados, el e-CF se emitió igualmente pero coincidió en huella con uno reciente.

Este es uno de los dos eventos cuyo data.object no es directamente el recurso, sino una estructura propia:

{
"ecf": { "...": "EcfDto" },
"previousEcfId": "...",
"previousEncf": "E310000000010"
}

Ver Endpoints y semántica para el detalle completo de cómo funciona la detección por huella.

Evento Cuándo se dispara
inbound_ecf.received Recibiste un e-CF de otro contribuyente y le devolviste el ARECF firmado, lo hayas aceptado o rechazado.
commercial_approval.received Un comprador externo aprobó o rechazó comercialmente un e-CF tuyo.

Ver Recibir un e-CF.

Evento Cuándo se dispara
certificate.expiring Tu certificado activo se acerca a su vencimiento (90, 30, 15 o 7 días antes).
certificate.expired Tu certificado venció.
sequence.expiring Un rango de e-NCF se acerca a su vencimiento (30 o 7 días antes).
sequence.expired Un rango venció.
sequence.low Un rango cayó al 20 % o menos de su stock.
sequence.exhausted Un rango se quedó sin números por entregar.

Ver e-NCF y secuencias.

Evento Cuándo se dispara
contingency.activated NovaFE entra en modo de contingencia (automático o manual).
contingency.deactivated Sale del modo de contingencia.

data.object es { status: "active" | "inactive", changedAt, oldestPendingMinutes }. A diferencia del resto, no es un evento de tu tenant puntual: se emite a todos los tenants suscritos por igual, porque la contingencia es un estado de la plataforma completa, no de un comprobante o de una cuenta.

Evento Cuándo se dispara
tenant.emission_paused Tú o tu organización pausaron la emisión de comprobantes nuevos.
tenant.emission_resumed Se levantó la pausa a mano.

data.object es { tenantId, paused, reason, until, source, changedBy, organizationId }, donde source es tenant u organization. Solo avisan cuando el estado efectivo cambia: cambiar únicamente el motivo no genera otro evento, y una pausa que termina sola por su hora de reanudación tampoco genera uno. Mientras la emisión está pausada, POST /ecf responde 409 con el código Ecf.EmissionPaused; lo ya firmado, los reintentos, la validación y las consultas siguen funcionando.

Evento Cuándo se dispara
alert.rejection_spike El porcentaje de comprobantes rechazados en la última hora llegó a tu umbral.
alert.emission_silence Pasaron las horas laborales que configuraste sin que emitieras ningún comprobante.

Las dos alertas están apagadas hasta que las configures (alerts.rejection_rate_threshold_pct, alerts.no_emission_hours y alerts.business_hours). Se evalúan cada 15 minutos.

alert.rejection_spike: data.object es { tenantId, windowMinutes, total, rejected, ratePct, thresholdPct }. Necesita al menos 5 comprobantes en la última hora, para que un solo rechazo no cuente como un 100 %, y se repite a lo sumo cada 6 horas mientras el pico siga en pie.

alert.emission_silence: data.object es { tenantId, lastEmissionAt, silentHours, thresholdHours }. Solo cuenta el tiempo dentro de tu horario laboral y avisa una sola vez por silencio: vuelve a avisar únicamente después de que emitas un comprobante nuevo. No avisa fuera del horario laboral, mientras la emisión está pausada, ni a un contribuyente que todavía no ha emitido nunca.

Evento Cuándo se dispara
tenant.monthly_limit_warning Tu emisión de Producción del mes llegó al 80 % del tope mensual.
tenant.monthly_limit_reached Llegó al 100 %: los comprobantes nuevos se rechazan hasta el mes siguiente.

data.object es { tenantId, period, limit, used }, donde period es el mes (yyyy-MM). Cada umbral avisa una sola vez por mes. Mientras el tope está alcanzado, POST /ecf responde 409 con el código Ecf.MonthlyLimitReached.

Evento Cuándo se dispara
usage.threshold_reached Tu organización consumió el 80 % de su cupo incluido de e-CF del período.
usage.limit_reached Consumió el 100 % de e-CF.
usage.tenant_threshold_reached El pico de tenants del período llegó al 80 % de tu cupo.
usage.tenant_limit_reached El pico de tenants llegó al 100 %.
usage.member_threshold_reached El pico de miembros de la organización llegó al 80 % de tu cupo.
usage.member_limit_reached El pico de miembros llegó al 100 %.
billing.period_closed Se cerró un período de facturación, con el resumen de las tres dimensiones.

Los cuatro primeros son el mismo aviso proactivo de excedente que describe Planes y precios, solo que por webhook en vez de tener que consultarlo tú.

webhook.ping no es un evento real de tu cuenta: es el que dispara POST /webhooks/{id}/ping para que pruebes un endpoint. No te puedes suscribir a él, y nunca llega por ninguna otra vía.