Callback fiscal
API
Callback fiscal
Endpoint inbound al que tu proveedor fiscal hace POST cuando un documento fiscal cambia de estado. Multipaís (BR, CO, EC, CL, AR, VE). Actualiza el estado fiscal de la orden dentro de Fire y — para Brasil — dispara los eventos de orden downstream.
POST
Callback fiscal
Este endpoint es inbound — tu proveedor fiscal le hace POST cada vez que un documento fiscal es autorizado, rechazado, denegado, cancelado o falla. Es la API canónica multipaís de fiscal callback que acepta payloads de BR, CO, EC, CL, AR, VE. Fire valida el payload, actualiza el estado fiscal de la orden en
Cómo complementa
Resultados negativos (
Para estados no autorizados, omite
Un
fiscal_documents y orders.fiscal, y — para Brasil — despacha eventos outbound order.invoiced / order.reversed a tus Integration Flows.
Este endpoint es asíncrono. Fire autentica, corre el guard de source-of-truth + tenancy, deduplica, pre-marca la orden como
processing y encola el callback — luego devuelve 202 Accepted con un webhookEventId (típicamente en menos de 100 ms). La actualización de fiscal_documents / orders.fiscal ocurre un instante después en un worker en segundo plano (normalmente en ~2 segundos). Para ver el resultado del procesamiento, consultá GET /v1/webhooks/events/{webhookEventId}. Los problemas corregibles por el cliente (payload inválido, eventId equivocado, tenant equivocado, una acción reusando el evento de otra) se rechazan sincrónicamente con 4xx antes del 202.Cómo complementa order.completed y order.cancelled
order.completed y order.cancelled llevan el estado de negocio de una orden. El callback fiscal lleva el estado fiscal (autorización SEFAZ / DIAN / SRI / SII / AFIP / SENIAT). Juntos forman este lifecycle:
Después de procesar el callback, el siguiente order.completed o order.cancelled para el mismo orderId refleja el estado fiscal actualizado en:
data.store.storeFiscalConfig— contexto del emisor (CNPJ / NIT / RUC / RUT / CUIT / RIF…)data.payments.metadata.fiscal— agregados fiscales totales (BR populado; otros paísesnull)data.orderLines[].metadata.fiscal— clasificación fiscal por línea (BR populado; otrosnull)
order.invoiced (o order.reversed) separado con las referencias del documento SEFAZ en data.fiscal.
Hoy, solo
order.invoiced y order.reversed están cableados como eventos outbound (Brasil). El endpoint valida y guarda callbacks para los 6 países (CO, EC, CL, AR, VE, BR), y el estado fiscal actualizado aparece en el siguiente evento order.completed / order.cancelled sin importar el país. Los eventos outbound para CO/EC/CL/AR/VE están en camino.Autenticación
Este endpoint requiere una API key con scopewebhooks:fiscal, vendor-scoped al account dueño de la orden. Las keys sin scope o solo a nivel account se rechazan con 403 Forbidden.
Tu API key de Fire. Générala desde el dashboard en Settings → API Keys → Developer keys, con scope
webhooks:fiscal y un binding de vendor para el account cuyas órdenes esta key puede actualizar.Opcional
Bearer <token>. No se requiere para este endpoint hoy, pero está reservado para futuros tokens emitidos por proveedor.Body de la petición
El body es un objeto JSON con estos campos top-level. La forma deldocument está discriminada por countryCode — ver Documento por país abajo.
Código de país ISO 3166-1 alpha-2. Uno de
BR, CO, EC, CL, AR, VE. Determina las reglas de validación del objeto document.Estado del documento. Uno de:
authorized— la autoridad fiscal aprobó el documentocancelled— un documento previamente autorizado fue canceladorejected— el documento fue rechazado (validación, schema, firma)denied— la autoridad denegó la solicitud (típicamente fallo permanente de regla de negocio)error— ocurrió un error no recuperable en el proveedor o autoridad
eventType es authorized o cancelled, el campo document es requerido. Cuando es rejected, denied o error, el campo error es requerido.El id propio de tu proveedor para esta entrega. Se guarda para auditoría/forense — no es la llave de idempotencia. Fire deduplica por
(orderId, eventId), así que podés enviar un providerEventId nuevo en cada reintento sin crear duplicados. Usa el ID nativo del evento del proveedor si está disponible; si no, un UUID.Timestamp ISO 8601 UTC de cuándo ocurrió el evento en la autoridad fiscal (no cuándo el proveedor envió el callback).
UUID de la orden en Fire. Coincide con
data.orderId en los eventos order.completed y order.cancelled. Fire lo usa para encontrar el documento fiscal existente.UUID de correlación y llave de idempotencia (junto con
orderId). Debe ser el event.id de un envelope V4 que Fire emitió para esta orden — ecoalo exacto; nunca lo inventes.- Chequeo de fuente de verdad. Un
eventIdque no referencie un evento de Fire para esa orden se rechaza con400antes del202. - Cada acción lleva su propio evento. Ecoa el
event.iddel evento que disparó esta acción — la emisiónorder.invoicedpara un callbackauthorized, el evento de cancelación/reverso para un callbackcancelled. Reusar eleventIdde una acción para otroeventType(ej. uncancelledque ecoa eleventIddelauthorized) se rechaza con409— una cancelación debe referenciar su propio evento, no colgarse del de la autorización. Ver Idempotencia y escenarios.
El documento fiscal autorizado / cancelado. Requerido cuando
eventType es authorized o cancelled. La forma varía por país — ver Documento por país.Contexto de error para resultados negativos. Requerido cuando
eventType es rejected, denied o error.Bag free-form para extras específicos del proveedor (respuesta raw, IDs internos, etc.). No validado; pasa al log de auditoría.
Documento por país
La forma requerida del campodocument depende del countryCode. Todas las variantes comparten los campos base de abajo (comunes a los 6 países) más los identificadores específicos del país requeridos por esa autoridad.
Campos base comunes
Aplican a cualquiercountryCode. docType y docSubtype son obligatorios; el resto son opcionales — envíalos cuando tu proveedor fiscal los tenga disponibles. Fire los persiste en fiscal_documents / orders.fiscal y los reemite en los eventos outbound order.invoiced / order.reversed (Brasil) y en el siguiente order.completed / order.cancelled.
| Campo | Tipo | Requerido | Notas |
|---|---|---|---|
docType | "invoice" / "cancellation" | ✓ | Clase del documento — invoice para emisión, cancellation para cancelación |
docSubtype | string | ✓ | Subtipo según país/proveedor (p. ej. nfce, nfe, factura, boleta, factura_a) |
pdfUrl | string (URL) | null | Enlace de descarga del PDF del documento (DANFE / representación gráfica) | |
xmlUrl | string (URL) | null | Enlace de descarga del XML canónico de la autoridad fiscal | |
emittedAt | string (ISO 8601) | null | Fecha/hora UTC en que la autoridad autorizó/emitió el documento | |
cancelledAt | string (ISO 8601) | null | Fecha/hora UTC de la cancelación — relevante cuando eventType es cancelled | |
totalAmount | number | null | Monto total bruto del documento | |
taxAmount | number | null | Monto total de impuestos | |
currencyCode | string | null | Código de moneda ISO 4217 — exactamente 3 letras (p. ej. BRL, COP, USD) |
pdfUrl y xmlUrl se guardan tal cual los envías — Fire no descarga ni re-hostea el archivo. Si tu proveedor firma estas URLs con expiración, ten en cuenta que el enlace almacenado puede caducar; descarga y persiste el artefacto por tu lado si necesitas acceso durable.- Brasil (BR)
- Colombia (CO)
- Ecuador (EC)
- Chile (CL)
- Argentina (AR)
- Venezuela (VE)
Documentos fiscales brasileños (NF-e / NFC-e). Usa este código de país al enviar callbacks desde tu proveedor fiscal o cualquier otro proveedor BR.Además de los campos base comunes (
El ejemplo de abajo incluye los campos base opcionales (
docType, docSubtype, pdfUrl, xmlUrl, emittedAt, etc.), BR requiere estos identificadores específicos:| Campo | Tipo | Requerido | Notas |
|---|---|---|---|
chaveAcesso | string | ✓ | Exactamente 44 dígitos — chave de acceso SEFAZ |
protocolo | string | ✓ | Protocolo de autorización SEFAZ |
numero | int / string | ✓ | Número de documento |
serie | int / string | null | Serie del documento (codificada en chave) | |
modelo | 55 / 65 | null | 55 = NF-e, 65 = NFC-e | |
cnpjEmitente | string | null | CNPJ del emisor |
pdfUrl, xmlUrl, emittedAt, totalAmount, taxAmount, currencyCode) — todos pueden omitirse, pero si tu proveedor los tiene, envíalos:Resultados negativos (rejected, denied, error)
Para estados no autorizados, omite document y provee error. El countryCode sigue aplicando (valida el routing); el documento no se requiere porque no hay artefacto autorizado.
Rechazado
Respuesta
En caso de éxito el endpoint devuelve202 Accepted — el evento fue autenticado, validado, deduplicado y encolado. Un 202 no significa que el documento fiscal ya se actualizó; eso ocurre de forma asíncrona en un worker en segundo plano. Usá el endpoint de status para confirmar el resultado final.
El body trae dos ids distintos para que no haya ambigüedad: eventId es el id que vos enviaste (devuelto como echo), webhookEventId es el id de Fire para el registro encolado.
Siempre
true cuando la petición fue aceptada y encolada.true cuando este (orderId, eventId) ya se ingresó con el mismo eventType — se devuelve el registro existente y no se re-encola nada. false para un evento nuevo.Echo del
eventId que enviaste (el event.id de la emisión de Fire). Usalo para casar este acuse con tu envío.El id de Fire para el registro encolado en
webhook_events. Pasalo a GET /v1/webhooks/events/{webhookEventId} para consultar el resultado del procesamiento. En un duplicado es el mismo id que se devolvió la primera vez.Status actual del registro en la cola —
queued → processing → processed (y retry / failed / dead / ignored).Timestamp ISO 8601 UTC de cuándo Fire recibió por primera vez este evento. Estable entre reintentos — útil como ancla de traza.
Resumen legible de lo que pasó.
Comprobar el resultado
Como el procesamiento es asíncrono, el202 solo confirma que el evento fue encolado. Para ver si el documento fiscal se actualizó, consultá el endpoint de status con el webhookEventId devuelto por el 202:
Ciclo de vida en la cola:
queued → processing → processed (listo) · failed / dead (se agotaron los reintentos) · retry (esperando el próximo intento) · ignored.Intentos de procesamiento hasta ahora.
En éxito, el resultado del worker — ej.
{ "kind": "updated", "documentId": "…", "receiptId": "…" }.{ "message": "…" } cuando el último intento falló; null si no.404 se devuelve para ids desconocidos — o ids de otro tenant — sin filtrar existencia. Autenticá con la misma key webhooks:fiscal que usaste para el callback.
Idempotencia y escenarios
Fire deduplica callbacks por(orderId, eventId) — no por providerEventId (que podés regenerar libremente). Un evento es único con su orden: el mismo (orderId, eventId) reenviado con el mismo eventType es un replay benigno; el mismo (orderId, eventId) con un eventType distinto es un intento de colgar una acción sobre el evento de otra, y Fire lo rechaza.
Esta es la matriz completa de comportamiento — cada combinación que podés enviar:
| Escenario | Resultado |
|---|---|
(orderId, eventId) nuevo | 202 · duplicate: false — encolado |
Mismo (orderId, eventId, eventType) reenviado (cualquier providerEventId) | 202 · duplicate: true — se devuelve el registro existente, no se re-encola |
Mismo (orderId, eventId) pero eventType distinto (ej. cancelled con el eventId del authorized) | 409 — rechazado. Usá el evento que pertenece a esta acción |
eventId no emitido por Fire para ese orderId | 400 — eventId incorrecto/inventado |
| Orden/evento fuera de la cuenta + vendor de tu API key | 403 |
| Payload malformado (Zod), faltan campos obligatorios | 400 |
| API key faltante / inválida | 401 |
| Auth store momentáneamente inalcanzable | 503 — transitorio, reintentá |
409 vs 202. Un 409 es el único caso donde un callback bien formado y autenticado se rechaza en la capa de idempotencia — porque aceptarlo divergiría el estado. Un replay del mismo tipo nunca es un error: devuelve 202 para que tus reintentos queden limpios. Un 503 es nuestro (infra transitoria), así que es seguro y esperado reintentar.Concurrencia
Las transiciones de estado enfiscal_documents usan locking optimista. Si dos callbacks para el mismo documento llegan concurrentes, uno tiene éxito y el otro resuelve a idempotent o regression según el orden. El merge de orders.fiscal JSONB es atómico.
Qué pasa después del 202
El202 solo encola el evento. Un worker en segundo plano lo toma — en ~2 segundos vía el wake-up instantáneo, o en el próximo ciclo de polling como fallback — y:
- La fila
fiscal_documentsse hace upsert con el nuevo status y referencias del documento (chaveAcesso,protocolo,cufe,cae, etc., según país). - La columna
orders.fiscalJSONB se mergea con la misma data — visible en el siguiente eventoorder.completed/order.cancelledpara el mismoorderId, sin importar el país. - Se despacha un trigger outbound — para Brasil,
triggerType = 'order.invoiced'(paraeventType=authorized) o'order.reversed'(paraeventType=cancelled). Los Integration Flows activos para ese trigger corren y hacen POST a tu endpoint.
- Hoy, solo
order.invoicedyorder.reversedestán cableados. Disparan cuando un callback authorized/cancelled para una tienda BR se procesa. - Para CO/EC/CL/AR/VE, los eventos outbound equivalentes aún no están cableados — los callbacks se validan y guardan, y el estado fiscal se refleja en el siguiente
order.completed/order.cancelledpara la misma orden.
Relacionado
order.completed
Ve dónde aterriza el estado fiscal dentro del snapshot V4 de la orden.
order.invoiced
Evento outbound BR-only disparado por este callback.
Inyectar orden
El endpoint de inyección que crea la orden que este callback actualiza.
Autenticación
Cómo funcionan API keys, scopes y vendor binding.

