Endpoints y semántica
Endpoints
Sección titulada «Endpoints»POST /api/v1/ecf emitirPOST /api/v1/ecf/validate validar sin emitirGET /api/v1/ecf/{id} estado y detalleGET /api/v1/ecf/{id}/xml XML firmado (?rfce=true para el RFCE)GET /api/v1/ecf/{id}/trackids trackIds registrados en la DGIIGET /api/v1/ecf/{id}/attempts historial de envíos a la DGIIGET /api/v1/ecf?... listado y búsqueda paginadosPOST /api/v1/ecf/{id}/retry reencolar un envío failed, review o rejected (si la DGII no consumió el e-NCF)Todos se autentican con el header X-API-Key (ver
Autenticación rápida). El detalle de
/validate está en Validar sin emitir, y el
de los GET en Consultar un comprobante.
Esta página cubre la semántica de POST /api/v1/ecf. El reenvío y el historial
de envíos se explican en Rechazos y reintentos.
Pausa y tipos permitidos
Sección titulada «Pausa y tipos permitidos»Desde Configuración puedes poner dos frenos a tu propia emisión (tu organización también puede fijarlos para todos sus contribuyentes):
| Situación | Respuesta |
|---|---|
| La emisión está pausada | 409 Ecf.EmissionPaused, con el motivo que escribiste. |
| El tipo de comprobante no está en tu lista | 403 Ecf.TypeNotAllowed. |
En ambos casos no se consume ningún e-NCF ni queda una clave de idempotencia pendiente, así que puedes reintentar con la misma clave cuando levantes el freno. La pausa solo frena emisiones nuevas: lo que ya firmaste se sigue enviando a la DGII, y la validación y las consultas siguen funcionando.
Ambientes que incluye tu plan
Sección titulada «Ambientes que incluye tu plan»Los planes gratuitos (Developer y Developer + Soporte) solo permiten el ambiente de pruebas. Con
uno de ellos, POST /ecf en certificación o en producción responde 409
Billing.EnvironmentNotIncluded, y tampoco puedes crear una API key de esos ambientes. No se
consume ningún e-NCF ni queda una clave pendiente. Para usar certificación o producción sube a un
plan de pago. Lo que emitas en pruebas y en certificación no cuenta contra el cupo de tu plan: solo
cuenta lo aceptado en producción.
Tope de sobreconsumo de la organización
Sección titulada «Tope de sobreconsumo de la organización»Si tu organización fijó un tope de sobreconsumo, POST /ecf responde 409 Billing.SpendCapReached
cuando el excedente del período (lo que pasa del cupo incluido en el plan) llega a ese monto. No se
consume ningún e-NCF ni queda una clave pendiente. La emisión se reanuda en el período siguiente o
cuando la organización sube el tope.
Topes de emisión
Sección titulada «Topes de emisión»Desde Configuración puedes limitar tu propia emisión (tu organización también puede fijar los topes para todos sus contribuyentes):
| Situación | Respuesta |
|---|---|
| Alcanzaste el tope mensual de Producción | 409 Ecf.MonthlyLimitReached, hasta el mes siguiente o hasta subir el tope. |
| Superaste tu máximo de emisiones por minuto | 429 Ecf.RateLimited, con el header Retry-After en segundos. |
El tope mensual solo cuenta los comprobantes de Producción; los de pruebas no cuentan. En los dos casos no se consume ningún e-NCF ni queda una clave de idempotencia pendiente, así que puedes reintentar con la misma clave. El límite del plan de tu organización sigue rigiendo por encima de tu máximo por minuto.
Ráfagas y saturación
Sección titulada «Ráfagas y saturación»NovaFE atiende un número limitado de emisiones a la vez. Si envías una ráfaga (por ejemplo, 40
comprobantes de golpe), las que sobran esperan su turno y se atienden en orden, sin que tengas que
hacer nada: solo tardan un poco más. Si la ráfaga es tan grande que también se llena la cola de
espera, la API responde 503 con el header Retry-After (en segundos). Es un aviso de saturación,
no un error de tu comprobante: no consume ningún e-NCF ni deja una clave pendiente, así que
reintenta con la misma Idempotency-Key pasado ese tiempo.
| Situación | Respuesta |
|---|---|
| Hay demasiadas emisiones en curso | 503, con Retry-After en segundos. Reintenta. |
Reintentos seguros con Idempotency-Key
Sección titulada «Reintentos seguros con Idempotency-Key»El header Idempotency-Key es obligatorio en cada POST /ecf: genera un
valor único por comprobante (cualquier string, por ejemplo un UUID). Si falta,
la API responde 400 Ecf.IdempotencyKeyRequired; si lo mandas en el cuerpo
JSON en vez del header, responde 400 Ecf.IdempotencyKeyInBody. La razón es
que un e-CF no se puede borrar: si una llamada se corta por timeout y
reintentas sin clave, se emitirían dos comprobantes. Con la clave, repetir el
mismo request es seguro:
| Situación | Qué pasa |
|---|---|
| Misma clave, mismo cuerpo | 200 OK con la respuesta del comprobante original. No se emite dos veces. |
| Misma clave, cuerpo distinto | 409 Ecf.IdempotencyKeyConflict. |
| Misma clave, la petición anterior todavía se está procesando | 409 Ecf.RequestInProgress. |
Si tu integración reintenta antes de que termine el RequestInProgress,
espera unos segundos y reintenta con la misma clave.
Si la emisión falla sin crear el comprobante (por ejemplo, no hay rango de secuencia disponible), la clave se libera y puedes reintentar de inmediato, con la misma clave y el cuerpo corregido.
Deduplicación por internalNumber
Sección titulada «Deduplicación por internalNumber»Si mandas internalNumber (tu propio número de factura) y ya existe un
comprobante con ese mismo número para tu tenant, NovaFE no emite uno nuevo:
te devuelve 200 con el comprobante existente. Es un mecanismo aparte del
Idempotency-Key, pensado para cuando tu sistema reintenta una operación
sin recordar qué clave de idempotencia usó la primera vez.
Detección de duplicados por huella
Sección titulada «Detección de duplicados por huella»Una tercera capa, independiente de las dos anteriores, para el caso en que
tu integración arma dos comprobantes distintos (con distinto
internalNumber y distinto Idempotency-Key) que en la práctica son el
mismo. Se activa por configuración de tu tenant
(ecf.duplicate_detection_mode, apagada por defecto):
off(por defecto): no corre ningún chequeo.observar: no bloquea nada. Si detecta una posible duplicación, emite el comprobante de todas formas y además dispara el webhookecf.duplicate_suspected, para que decidas tú qué hacer.bloquear: si detecta una posible duplicación, responde409Ecf.DuplicateSuspecteden vez de emitir.
La huella compara comprador, tipo de comprobante, monto total, fecha de emisión y ambiente, contra los comprobantes emitidos en una ventana reciente (5 minutos por defecto, configurable entre 30 segundos y una hora).
El recorrido de una emisión
Sección titulada «El recorrido de una emisión»POST /ecf resuelve tu emisor y ambiente desde la API key, revisa
idempotencia y duplicados, asigna la secuencia, arma y calcula el
comprobante, lo firma con tu certificado, y persiste y encola todo en una
sola transacción antes de tocar la DGII. Desde ahí hay dos modos, según la
configuración de tu cuenta:
| Modo | Qué hace POST /ecf |
Cómo conoces el resultado |
|---|---|---|
| Con espera (el modo por defecto) | Intenta el envío dentro del propio request, durante unos 8 segundos. Si la DGII responde a tiempo, la respuesta ya trae el estado final y el bloque dgii. Si tarda más, responde con submitted o signed y un proceso de fondo termina el seguimiento. |
En la respuesta, o después por webhook o consultando el comprobante. |
| Sin espera (asíncrono) | Responde de inmediato con el comprobante firmado: status: "signed" y dgii: null. El envío a la DGII ocurre en segundo plano. |
Siempre por webhook (ecf.submitted, ecf.accepted…) o consultando el comprobante. |
El modo sin espera es útil para sistemas intermedios con muchas cajas
emitiendo en paralelo, que prefieren no mantener peticiones abiertas. Se
activa con el ajuste submission.wait_for_result, que puede fijar tu
organización o tu contribuyente, y que NovaFE puede desactivar de forma
global ante un incidente. Ver Ajustes del contribuyente.
Errores
Sección titulada «Errores»Todo error de la API (de negocio, de validación, o inesperado) responde
application/problem+json, con esta forma base:
{ "type": "https://tools.ietf.org/html/rfc9110#section-15.5.5", "title": "El e-NCF solicitado no existe.", "status": 404, "traceId": "b1f6c3e2-8a2d-4e10-9c7a-4f2e6d1a9b30"}Con un solo error, la descripción va en title y no hay ningún otro campo
con el motivo. Con más de uno a la vez, se agrega errors: una lista de
{ code, description } para errores de negocio, o un diccionario
{ campo: [mensajes] } cuando es un error de validación de formulario.
traceId identifica la petición de punta a punta y es el dato a dar si
necesitas soporte. Por defecto lo genera NovaFE, pero puedes fijar el tuyo
mandando el header X-Trace-Id: la respuesta lo devuelve tal cual, así
correlacionas tus propios logs sin depender del valor que generó la API.