Skip to main content
POST
Fire resuelve por dentro qué proveedor fiscal corresponde al país de la tienda, le pide la numeración y te devuelve los datos listos para imprimir.
Tres cosas antes de integrar:
  1. Acá no hay id de orden. Al cobrar, la orden todavía no existe en Fire. El orderCode es lo único que liga esta solicitud con la venta, así que tiene que ser el mismo string que después viaja en la inyección.
  2. La respuesta no dice que el documento esté autorizado. Dice que hay números para imprimir. La autorización del ente llega después, de forma asíncrona.
  3. Pedís una operación, no un tipo de documento. INVOICE o CANCEL. Con qué instrumento fiscal se materializa —factura, nota de crédito, evento de cancelación— lo decide el país, y no es asunto del punto de venta.

El flujo completo

1

Cobrás

El cliente paga en el POS o el kiosco.
2

Pedís la numeración

Llamás a este endpoint. Fire resuelve la tienda, el emisor y el proveedor, y guarda la solicitud antes de salir a pedir los números.
3

Imprimís

Según printing.mode imprimís el comprobante fiscal o un ticket provisional.
4

Inyectás la orden

Con el cuerpo de siempre, sin agregarle nada. Fire correlaciona la venta con su numeración por el orderCode.
5

El ente autoriza

Minutos después. Fire recibe el resultado del proveedor y actualiza la orden. Si querés verlo, consultá esta misma solicitud.
Fire nunca te frena por su cuenta. Falle lo que falle, este endpoint contesta con una decisión explícita en policy.numberingFailure.action — no con un error que te deje sin saber qué hacer.Pero la decisión no siempre es seguir: la cuenta puede configurar que sin comprobante no se vende. Ramificá por action, nunca por el código HTTP:
  • CONTINUE (el default) — imprimís según printing.mode e inyectás la orden igual. Si quedó sin numerar, volvé a llamar con el mismo orderCode: nadie la completa sola, y un 202 que nadie reintenta se queda así para siempre.
  • REFUND — devolvés el cobro y no inyectás la orden. No hay nada que completar después: reintentar la numeración de una venta que devolviste generaría un comprobante para algo que no ocurrió.
En los dos casos: no retengas la venta ni reintentes en bucle con el cliente esperando.Cuántas veces reintentar, en el camino CONTINUE: dos. Mientras retryable venga en true, volvé a llamar con el mismo orderCode hasta dos veces más. Si al segundo reintento sigue sin numerar, tratalo como definitivo: la venta ya está inyectada con ticket provisional y lo que falta se resuelve por soporte, no en el mostrador.Con REFUND no hay reintento: cero. La venta se devolvió, y numerarla después generaría un comprobante de algo que no ocurrió.El tope lo aplicás vos. Fire numera cada intento y lo guarda para soporte, pero no corta por su cuenta: si llamás una cuarta vez, le vuelve a preguntar al proveedor igual. Y la respuesta no va a cambiar por insistir — action no depende de retryable, así que lo que diga el tercer intento ya lo decía el primero.

Headers

string
requerido
Tu API key de Fire, con el permiso Fiscal Gateway (numbering).La cuenta y el vendor se derivan de la key, nunca del cuerpo. Por eso el payload no lleva accountId ni vendorId: una credencial no puede mentir sobre a quién pertenece.
string
requerido
UUID que identifica este intento. Generala una vez por venta y reusala en los reintentos de esa misma venta.
No es lo que evita el documento duplicado — eso lo hace el orderCode, que es la llave natural en los dos extremos: si repetís el mismo orderCode, recibís el mismo documento aunque generes una llave nueva.Lo que esta llave aporta es detectar que la reusaste para otra venta: si llega la misma llave con un cuerpo distinto, Fire responde 409 en vez de numerar. Es un guard contra un bug del punto de venta —no regenerar la llave— que sin esto pasaría inadvertido.
string
Opcional. Un identificador tuyo para esta operación — el que ya usás en tus logs.Fire lo guarda con la solicitud y lo devuelve en correlationId. No cambia nada del comportamiento: sirve para que, cuando algo falle, puedas cruzar tu registro con el nuestro sin tener que emparejar por hora y orderCode.Si no lo mandás, correlationId viene en null.

Cuerpo

Es un payload propio, no el de la inyección de órdenes: solo lo que hace falta para numerar. Los nombres coinciden con los que ya usás (store, device, orderCode) para que lo armes recortando lo que ya tenés, pero no envíes el cuerpo completo de la orden — nada de products, payments ni shippingMethod. Siete campos, y ninguno es un código del ente.
string
requerido
Código de la venta. Es la clave de idempotencia, la de Fire y la del proveedor.Tiene que ser único por cuenta y país, y el mismo que después envías al inyectar la orden. Si dos tiendas de la misma cuenta usan el mismo orderCode, Fire corta con 409 antes de emitir: sin ese corte, la segunda tienda imprimiría el secuencial de la primera.
string
requerido
Fecha y hora de creación de la orden.Tiene dos usos, y conviene no confundirlos. Es el respaldo de la fecha de emisión —la fuente principal es el día de negocio abierto de la tienda, porque una venta de la madrugada pertenece al día que sigue abierto, no al del reloj—, y además viaja al proveedor fiscal como la hora en que ocurrió la venta, para los regímenes que la exigen en el comprobante.Si no lleva zona horaria (2026-08-12 17:26:09), se interpreta como UTC. Al proveedor sale siempre normalizada, con Z.
string
predeterminado:"INVOICE"
Qué se pide. Uno de:
  • INVOICE — numerar la venta.
  • CANCEL — anular.
Pedís una operación, no un tipo de documento. Con qué instrumento fiscal se materializa lo decide el país: en Ecuador una anulación es una nota de crédito con su propia serie de secuenciales; en Brasil es un evento de cancelamento que no produce comprobante nuevo.
Para anular mandás el mismo orderCode de la venta, con operation: "CANCEL". Nada más: ni claves fiscales, ni el número del documento original, ni identificadores de Fire.Fire encuentra el documento a compensar por la llave natural —país + orderCode + operación— y te devuelve a cuál corresponde en document.compensates. Tu punto de venta no necesita guardar nada nuestro para poder anular.
object
requerido
La tienda que emite. Con code, Fire resuelve el país, la identidad fiscal del emisor y el establecimiento — no los envíes vos.
object
requerido
El aparato que emite. Es el mismo bloque que ya mandás en la inyección de órdenes — no hay que agregarle nada.
No declares el punto de emisión. Antes había un externalId con el que el canal lo informaba; se quitó. Lo asigna el ente bajo el RUC del emisor y el punto de venta no habla ese idioma: ahora lo resuelve el proveedor a partir del uid, igual que hace con store.code.Viene de vuelta en countryData.puntoEmision, con el que quedó efectivamente emitido.
array
Lo que se cobró. Es el mismo payments.totals que ya mandás en la inyección de órdenes — mandalo tal cual, entero.
Lo que se manda hoy, por país:Mandá taxes[] con amount, no un porcentaje suelto. Un elemento por impuesto, con name, base, rate y amount — la misma forma en todos los países.El monto tiene que venir calculado por vos, que sos quien lo cobró e imprimió. Donde el identificador fiscal es un hash de la factura —el CUFE colombiano— derivarlo del porcentaje obliga a alguien más a redondear, y si redondea distinto que la caja, el identificador deja de corresponder al papel que tiene el cliente.
Los importes van sin escalar. Mandá el número tal como lo cobraste: 50000, 42016.81. No lo multipliques por 10.000.Esa escala existe, pero es de otro camino: los eventos de la orden (order.completed y los demás) llevan los mismos importes como entero en string ×10.000, porque es como FIRE los almacena. Acá no.Si integrás los dos caminos, esa es la única conversión que tenés que hacer — y hacerla al revés significa declarar diez mil veces el monto.
Mandá el importe tal como lo cobraste e imprimiste. Fire no lo redondea ni lo reformatea: el número del request y el del comprobante son el mismo.Importa porque hay regímenes donde el identificador fiscal es un hash de la factura — el CUFE colombiano, por ejemplo. Si el importe que entra al hash no es el que está impreso, el identificador no corresponde a la factura que tiene el cliente en la mano.
object
Quién compró. Es el mismo bloque que ya mandás en la inyección de órdenes: mandalo entero, tal cual. No lo recortes, no lo renombres, no lo traduzcas.Fire lee de ahí lo que el régimen del país necesita y descarta el resto. Que sea el bloque completo y no un subconjunto es a propósito: si cada país exigiera su propio recorte, el punto de venta tendría que saber qué campo mira cada ente — que es exactamente lo que este contrato evita.El resto de los campos —uid, email, phone, gender, birthdate, externalId— viajan y no se fiscalizan. Fire no los reenvía al proveedor fiscal: no son asunto del ente.
Los valores que Fire espera en govIdType:Hoy no hay guard: mandes lo que mandes, la venta se numera. El campo viaja tal cual al proveedor fiscal, así que un valor fuera de esta lista no rompe la numeración — le llega a él, que es quien tiene que reconocerlo.Por eso conviene ajustarse: un CEDULA donde va CI, o dos escrituras distintas para el consumidor final, son documentos que salen mal sin que nada falle en el camino.
Consumidor final: mandá los dos campos, sin traducir ninguno.
El número lo mandás vos, igual que en cualquier otra venta. Lo que no tenés que hacer es convertirlo a lo que exige cada régimen: el NIT genérico 222222222222 de la DIAN en Colombia, la ausencia de destinatario en Brasil. Eso lo resuelve el proveedor fiscal, que es quien está certificado ante el ente.Es deliberado: esa regla cambia por país y por resolución del ente, y no debería obligarte a desplegar el punto de venta cuando cambie.Fire tampoco lo toca. El bloque client viaja tal cual al proveedor: no completamos el número, no lo normalizamos y no lo validamos. Lo que mandás es lo que él recibe.
object
Llave-valor de la venta: lo que cambia en cada transacción y que algún país exige.Es opaco para tu integración: Fire no lo interpreta, lo transporta. Las claves válidas dependen del país de la tienda y una clave desconocida se rechaza con 400 — es preferible un error de Fire a un campo inventado viajando al ente.En la mayoría de los casos va vacío: lo que es constante de la tienda no se manda acá, se configura una vez (ver abajo).

Lo que NO se manda: la configuración de la tienda

Todo lo que es constante de la tienda se configura una sola vez en el backoffice y viaja solo: la identidad fiscal del emisor, y un bloque llave-valor por país para los atributos que el proveedor de ese país necesite.
Ese llave-valor vive en la configuración fiscal de la tienda, separado por país. Es la razón por la que este endpoint es el mismo en todos lados: lo específico de cada país se administra, no se programa ni se envía en cada venta.Si tu integración empieza a necesitar un campo nuevo por país, la respuesta casi siempre es configurarlo ahí — no agregarlo al payload.
string
Solo para CANCEL, y solo cuando la resolución automática no alcanza: una anulación que referencia un documento de otra orden, o varias facturas para la misma.En el caso normal no lo envíes. Fire encuentra el original por la clave natural, así que tu punto de venta no necesita guardar ningún identificador nuestro para poder anular.

Respuesta

string
Identificador de la solicitud en Fire. Es con el que consultás el desenlace después.
string
Eco del código que enviaste.
string
Eco del header x-correlation-id, o null si no lo mandaste. Es para trazabilidad: no interviene en la numeración ni en la idempotencia.
boolean
true si esta solicitud ya existía y se devolvió tal cual, sin numerar de nuevo.
string
Estado de la numeración: ¿conseguí números para imprimir?Cinco valores posibles. Los cuatro primeros describen cómo terminó el intento; el quinto dice que no hubo intento porque esta tienda no numera.
PENDING no significa “no hay comprobante”: significa “no sabemos”. Se cortó la comunicación y el proveedor pudo haber numerado, consumido un secuencial y emitido el documento sin que nos enteremos.Reintentá con el mismo orderCode. Fire retoma la solicitud y vuelve a preguntarle al proveedor; si la primera vez numeró, recibís ese mismo documento en vez de uno nuevo. Numerar de nuevo con otro orderCode sería declarar la misma venta dos veces ante el ente.
NOT_APPLICABLE es el único que no se guarda: no crea solicitud fiscal (fiscalRequestId: null) y no aparece en los eventos de la orden. Existe porque el POS llama siempre a este endpoint —es como descubre si la tienda numera— y contestarle un error haría que cada venta de una tienda sin gateway pareciera una falla.
string
Estado del documento ante el ente: ¿lo autorizó?PENDING · AUTHORIZED · REJECTED · CANCELLED
En esta respuesta es siempre PENDING: hay números, no hay veredicto. Solo lo mueve el resultado del proveedor, que llega después. Colapsar los dos estados en uno es el error que hace que un POS crea que una venta está autorizada cuando solo está numerada.
string
En qué ambiente numeró Fire: SANDBOX o PRODUCTION.Es nuestro, no del ente. Sale de la configuración fiscal de la cuenta —que es por vendor y por país, así que la misma cuenta puede tener Ecuador en producción y Colombia en sandbox— y queda congelado en la solicitud: si mañana se cambia la configuración, este valor sigue diciendo con qué numeró esta venta.
No lo confundas con el ambiente del ente, que viaja dentro de countryData con el vocabulario del país (ambiente: "PRUEBAS" | "PRODUCCION" en Ecuador). Son dos hechos distintos: uno dice contra qué configuración emitió Fire, el otro qué declaró el organismo. Normalmente coinciden — y cuando no, eso es exactamente lo que hay que poder ver, por eso no se deduce uno del otro.
null cuando requestStatus es NOT_APPLICABLE: no se numeró, así que no hubo ambiente en el que numerar.
object
Lo que necesitás para imprimir, sin saber de países. null si no se numeró.
sequential y serie ya no están acá. Son piezas con forma de país —en Ecuador la serie son seis dígitos que se parten al medio— y viven en countryData con el nombre que les da su ente. En document quedó solo lo que significa lo mismo en todos lados.
object
Los identificadores del país, en el vocabulario de su ente y listos para imprimir.
El bloque cambia entero según el país, y los rótulos también. En Colombia documentLabel es FACTURA ELECTRONICA DE VENTA y authorizationLabel es VALIDACION PREVIA — son los nombres de la DIAN, no una variante del texto ecuatoriano.ambiente llega traducido en los dos países, y ahí está lo importante: la DIAN codifica 1 como producción y el SRI como pruebas. Fire lo resuelve para que ningún canal tenga que llevar esa tabla.
Es un mapa abierto: las claves las define el régimen de cada país, no este contrato. Un país nuevo entra sin que cambie la forma de la respuesta.
Los valores vienen traducidos, no en código del ente. El proveedor manda ambiente: "2" —así lo define el SRI— y acá llega "PRODUCCION", que es lo que dice el ticket. Traducirlo del lado del canal significaría que cada integrador lleva su copia de la tabla del ente, y el primero que la copie mal imprime “PRUEBAS” en una factura de producción.
No busques campos fijos: recorré las claves que vengan. Ecuador trae claveAcceso, Colombia cufe, Brasil chaveAcesso. Un canal que lea countryData.claveAcceso a secas funciona en Ecuador y se rompe en el segundo país.

countryData por país

Hoy el gateway numera en Ecuador y Colombia. Cada país que entra suma su pestaña acá — y solo eso: la forma de la respuesta no cambia, porque el bloque es abierto.
Comprobantes del SRI.
El número lo arma el proveedor, no Fire. El formato es del régimen —quince dígitos en tres tramos, art. 18 del Reglamento de Comprobantes de Venta— y lo conoce quien está certificado ante el SRI. Si el régimen cambia la convención, cambia allá y no hace falta que Fire despliegue.document.documentNumber es un eco de este mismo valor, para que no tengas que entrar al bloque del país solo para imprimir. Es el mismo hecho, no dos.
Las tres piezas sueltas no son un sustituto. El Reglamento permite omitir los ceros a la izquierda del secuencial, así que 001-020-123 puede ser tan legal como 001-020-000000123. Componer el número vos mismo a partir de establecimiento, puntoEmision y secuencial es adoptar una convención que no te corresponde: imprimí numeroComprobante tal como llega.
object
La sucursal que emite, para el encabezado del comprobante.
object
La persona jurídica que emite: el encabezado y el pie del comprobante, ya resueltos.
store, company y document.compensates vienen en toda respuesta de este endpoint, incluida la del reintento idempotente, la de NOT_APPLICABLE y la del 400 de tienda que no puede emitir — el canal necesita el encabezado tanto la primera vez como cuando repite por un corte de red, y sobre todo cuando tiene que imprimir un provisional.Las consultas (GET por fiscalRequestId u orderCode) los devuelven en null: se resuelven al emitir y no se guardan con la solicitud. Si tu integración los necesita para reimprimir, usá los datos de impresión.
object
Llave → string exacto a codificar, listo para renderizar. Por ejemplo { "qr": "1208202601…811" }.Es un mapa abierto porque el comprobante de cada país no lleva siempre lo mismo, y un país puede necesitar más de un elemento. Recorré las claves que vengan, no busques campos fijos.Fire no genera imágenes: el tamaño y la resolución dependen de tu impresora, y eso solo lo sabe quien imprime.
object
Qué podés imprimir. Es una regla legal del país, no una derivación de si hay documento: por eso la resuelve Fire y no cada canal.
object
Qué hacer con la venta si no se pudo numerar. Lo decide la cuenta, no vos: se configura por vendor en el backoffice y Fire te devuelve la decisión ya tomada, igual que printing.Viaja siempre, también cuando la numeración salió bien. Ramificá por valor, nunca por presencia de la clave.
Qué decide esta política, y qué no.Decide una sola cosa: si la caja le devuelve el dinero al cliente cuando la venta se cobró y no se pudo numerar. Nada más.No decide qué imprimís —eso es printing, y es una regla legal del país, no una preferencia de nadie—. No bloquea ventas: cuando pedís la numeración el cliente ya pagó, así que no hay venta que bloquear. Y no depende de retryable: un fallo que se arregla solo sigue siendo un fallo, y si la cuenta configuró devolver, se devuelve.La configura la cuenta, por país y por vendor, en el backoffice. Vos no la deducís ni la negociás: Fire te la devuelve resuelta, igual que printing. Si no está configurada, si trae un valor que no reconocemos, o si no la pudimos leer, se aplica CONTINUE — el default apunta para ese lado a propósito, porque una configuración mal escrita no puede disparar devoluciones de dinero.Dos casos la ignoran por completo, sin importar cómo esté configurada: cuando no hubo fallo (GENERATED o NOT_APPLICABLE), y cuando lo que falló era una anulación (operation: "CANCEL") — ahí la orden existe y su dinero no se devolvió, así que no hay nada que devolver.Los otros dos campos son el recibo de la decisión: configVersion dice con qué configuración se decidió y resolvedFrom con qué contexto. Sirven para reconstruir una devolución de hace tres semanas aunque hoy la cuenta esté configurada distinto.
Dónde se configura y qué pasa si no está. La política se carga por cuenta, país y vendor. Si el vendor no la tiene, si trae un valor que no reconocemos, o si no pudimos leerla, se aplica CONTINUE — y ese default apunta a propósito para ese lado: una configuración mal escrita no puede disparar devoluciones. Lo vas a ver como configVersion: null.
Las anulaciones nunca piden devolver. Si lo que falló era un operation: "CANCEL", la respuesta trae CONTINUE sin importar la configuración: no hay cobro que devolver, porque la venta ya había ocurrido y sigue vigente. Lo que falta es el documento de la anulación.PENDING sí obedece la configuración. Que el proveedor no haya contestado no es una excepción: para la caja, no tener número es no tener comprobante. Lo distinguís de un rechazo sólo por resolvedFrom.requestStatus. Un PENDING trae failureCode igual que los demás —un timeout llega como PROVIDER_TIMEOUT / TECHNICAL—, así que no lo busques en la ausencia del código.Tiene una consecuencia que conviene tener presente: el proveedor pudo haber numerado igual y no habernos podido avisar. Si ese documento aparece después, va a existir un comprobante de una venta que devolviste, y hay que anularlo.

Qué hacer cuando llega REFUND

Tres pasos, en este orden. El tercero es el que se olvida.
1

Devolvé el cobro en el mostrador

El cliente ya pagó. Esa devolución la hacés vos con tu medio de pago — Fire no mueve dinero ni sabe si lo devolviste.
2

No inyectes la orden

No la mandes a Crear orden. Esa venta no ocurrió: inyectarla dejaría una orden cobrada sin comprobante fiscal, que es peor que no tenerla.
3

Reportala con Registrar venta perdida

POST /orders/lost-sales, copiando policy.numberingFailure.lostSaleReason en reason. Es el único paso que nos avisa a nosotros.
Si no hacés el paso 3, esa venta no existe en ningún lado.No hay orden —no la inyectaste— y no hay evento. Del lado nuestro sólo queda la solicitud fiscal que falló, que dice que no se pudo numerar pero no dice que hubo plata de por medio ni que la devolviste. Nadie se entera de que esa tienda dejó de vender, y el cierre de caja no lo puede explicar.El reporte es la única traza. Reintentar es seguro —es idempotente por orderId + vendor—, así que si te quedaste sin red justo ahí, acumulá y reenviá.

Cuando lo que falla es una anulación

Anular son dos llamadas, en este orden: primero pedís acá la numeración de la nota de crédito (operation: "CANCEL"), y recién después llamás a Cancelar orden. Si la numeración de la nota falla, este endpoint responde igual que siempre: nunca un 4xx. PENDING y FAILED_RETRYABLE vuelven 202; FAILED_FINAL vuelve 200. El cuerpo trae el failure con el motivo y el fiscalRequestId para escalar. Y policy.numberingFailure.action siempre viene CONTINUE, con lostSaleReason: null, sin importar cómo esté configurada la cuenta. No es una excepción caprichosa: acá no hay cobro que devolver. La venta ya ocurrió, está en orders y sigue vigente — lo que falta es el papel de la anulación, no la plata.
Pero la orden no se cancela. El cancel valida que exista la nota de crédito, y sin ella responde 409 FISCAL_CREDIT_NOTE_MISSING.Esa es la diferencia grande con una venta: en la venta el fallo te deja seguir con un ticket provisional; en la anulación te deja frenado, con la orden todavía vigente.Qué hacer: si retryable viene en true, volvé a llamar acá con el mismo orderCode —Fire retoma la solicitud—. Si es FAILED_FINAL, leé failure.scope y escalá con el fiscalRequestId: no hay nada que la caja pueda hacer, y nadie lo reintenta por vos.
Un FAILED_FINAL en la nota de crédito no se arregla esperando. Esa solicitud queda guardada como definitiva, y volver a llamar con el mismo orderCode —aunque el proveedor ya esté sano— te devuelve la misma respuesta sin volver a preguntarle. No es un reintento que falla: es la respuesta archivada.La consecuencia es que esa orden no se puede cancelar más por este camino: sigue vigente en Fire, con su factura, y Cancelar orden responde 409 FISCAL_CREDIT_NOTE_MISSING para siempre. Escalar acá no es “avisá y reintentá más tarde” — es avisá, porque esto ya no se destraba solo.
object
Por qué no hay documento. null cuando sí lo hay.
PROVIDER_TIMEOUT y PROVIDER_CONTRACT_VIOLATION llegan con requestStatus: "PENDING", no con un fallo definitivo. En los dos casos el proveedor pudo haber numerado sin que podamos leerlo: emitir otro comprobante por afuera declararía la misma venta dos veces ante el ente.
string
Nuestro identificador de adaptador (hio), no el nombre del proveedor. Es lo que dice con qué integración se numeró esta venta. null en NOT_APPLICABLE: no intervino ninguno.
object
Quién numeró, del lado del proveedor. Tiene forma —los tres campos son parte del contrato— y por eso viaja separado de la bolsa opaca. null cuando no se numeró.
object
La bolsa de diagnóstico del proveedor, tal cual llegó. Opaca: no tiene forma garantizada y nadie debe programar contra sus claves — cambian sin previo aviso y sin versionar el contrato. Sirve para pegarla en un ticket, no para ramificar.null cuando el proveedor no mandó nada.
Es el mismo campo que viaja en el evento, con el mismo nombre y el mismo contenido. Los tres campos del proveedor —providerCode, providerIdentity, providerMetadata— se leen igual acá y en fiscalRepresentation: lo que aprendés en un extremo sirve en el otro.

Códigos de estado

El código HTTP no dice si conseguiste comprobante. 200 puede ser un reintento idempotente que numeró perfecto, o un rechazo definitivo del ente. Ramificá por printing.mode y requestStatus, nunca por el código solo.
La llamada es síncrona, pero tiene un presupuesto de tiempo. El cliente está parado en la caja: Fire espera al proveedor unos segundos y, si no contesta, corta y devuelve 202 en vez de dejar la venta colgada.Ese 202 no es una promesa de que después llega por otro canal a tu POS: es Fire diciendo “no tengo números todavía, imprimí provisional y seguí”.Para completarla, reintentá con el mismo orderCode. Fire retoma la solicitud y vuelve a preguntarle al proveedor. Es raro, pero existe porque la alternativa —fallar la venta— es peor.

Qué hacer con cada respuesta

Lo que sigue describe el camino CONTINUE, que es el default y el de la mayoría de las cuentas. Si policy.numberingFailure.action dice REFUND, la instrucción se invierte: no inyectás la orden y no reintentás la numeración — devolvés el cobro y reportás la venta perdida.Lo demás de cada estado —qué significa y si el fallo se arregla solo— vale en los dos casos.
printing.mode: "FISCAL_DOCUMENT". Imprimí el comprobante con document.documentNumber y dibujá los códigos de graphic. Inyectá la orden con el mismo orderCode.Si printing.reason es ISSUED_OFFLINE, el comprobante es válido pero se emitió en contingencia: imprimí la leyenda que corresponda en ese país.
El proveedor no contestó: timeout o conexión cortada. Pudo haber numerado y consumido un secuencial sin que nos enteremos.Imprimí el ticket provisional e inyectá la orden. Después reintentá con el mismo orderCode: Fire retoma la solicitud y vuelve a preguntarle al proveedor, así que si la primera vez numeró, recuperás ese documento.
No asumas que la venta quedó sin comprobante. Emitir uno nuevo por otro camino puede declarar la misma venta dos veces ante el ente.
El proveedor contestó que no puede ahora. A diferencia de PENDING, acá sabemos con certeza que no se numeró nada.Imprimí provisional, inyectá la orden y reintentá con el mismo orderCode.
Reintentar con el mismo cuerpo va a dar lo mismo. Leé failure.message, que trae el motivo real, y failure.scope, que dice a quién le toca arreglarlo:
  • FUNCTIONAL — hay un dato que el ente no acepta. Se corrige en la venta o en la configuración de la tienda.
  • TECHNICAL — la integración con el proveedor está rota. La venta está bien; lo que falla es la conexión con quien numera. Nadie en la caja puede resolverlo.
En los dos casos: imprimí el ticket provisional, inyectá la orden y escalá con el fiscalRequestId. Con CONTINUE, la venta queda cobrada sin comprobante fiscal — eso también viaja en los eventos de la orden, para que puedas compensarlo. Con REFUND no hay orden ni evento: la única traza es el reporte de venta perdida.
PROVIDER_AUTH_FAILED no es una caída del proveedor. Contestó, y rápido: en el ejemplo, 742 ms. Lo que rechazó es nuestra credencial —equivocada, revocada o rotada del otro lado—, así que es configuración y no algo transitorio: por eso retryable es false y el estado es FAILED_FINAL y no PENDING.Si en vez de esto ves PROVIDER_TIMEOUT con requestStatus: "PENDING", ahí sí el proveedor no contestó a tiempo — y ahí sí conviene reintentar.Fijate también en el bloque company del ejemplo: llega la identidad para el encabezado, pero countryLines y legends vienen vacíos porque no hay comprobante que declare nada.
No es un error. Este vendor no tiene representación fiscal: no hay nada que numerar y no se creó ninguna solicitud (fiscalRequestId: null).Imprimí tu ticket habitual e inyectá la orden con normalidad. Es la respuesta esperada para agregadores, países sin gateway fiscal y comercios con la numeración desactivada.
store y company vienen igual acá. No se numeró nada, así que llega la identidad del emisor y no el aparato fiscal (countryLines y legends vacíos). Es el mismo bloque que en cualquier otra respuesta: no hay una forma distinta que aprender para este caso.
En estos casos no se creó ninguna solicitud fiscal: corregí y volvé a llamar.Si recibís 404 con un mensaje de tienda no encontrada, revisá que tu API key sea la del vendor dueño de esa tienda — el mensaje incluye contra qué vendor se buscó.El 400 de tienda que no puede emitir trae la identidad del emisor en data. Es el caso de una tienda sin RUC o sin habilitar: la venta ya ocurrió y tenés que imprimir un provisional igual, así que el encabezado viaja con el error.
Llamalo siempre. Esta es tu forma de saber si la tienda numera.No necesitás sincronizar configuración ni decidir por país: si ese vendor no tiene representación fiscal, la respuesta es 200 con requestStatus: "NOT_APPLICABLE" y printing.mode: "NONE" — imprimís tu ticket y seguís. No es un error y no se crea ninguna solicitud.Es la misma rama que ya tenés: ramificás por printing.mode, no por el código HTTP.

Anular

Mandás el mismo orderCode de la venta con operation: "CANCEL". Nada más. Mandá el mismo cuerpo, totals y client incluidos. La anulación no es un borrado: emite un documento nuevo —una nota de crédito— que el proveedor calcula con los mismos datos que la factura. La anulación es total en todos los países: no existen anulaciones parciales, así que los importes que mandás son los de la venta completa. Qué documento se compensa lo resuelve Fire por el orderCode — eso sí, no lo mandes vos.
La respuesta tiene la misma forma que la de una factura. Cambian tres cosas:
La nota de crédito tiene su propia numeración. No continúa la de las facturas: en el ejemplo, la factura es 005-004-000000068 y su anulación 005-004-000000002. Son dos secuencias distintas bajo el mismo establecimiento y punto de emisión.
La anulación es un comprobante fiscal nuevo, no un borrado. La factura original sigue existiendo ante el ente y hay que conservarla: lo que hace la nota de crédito es compensarla.Por eso GET /numbering?orderCode=… devuelve dos documentos para esa orden.

Si no hay nada que anular

Si la venta nunca se numeró —porque la tienda no factura, o porque la numeración falló— la anulación responde 400:
Es un 400, no un fallo dentro de un 200. Es la única diferencia importante entre anular y facturar: al facturar, un rechazo del ente viaja como respuesta exitosa con requestStatus: "FAILED_FINAL", porque es la respuesta a tu pregunta. Acá no hay pregunta que responder — pediste compensar algo que no existe.Tu punto de venta imprime su comprobante de anulación interno y sigue.

Cuando la anulación no se resuelve en el momento

Anular tiene los mismos desenlaces inciertos que facturar, y conviene decirlo porque es fácil asumir que anular siempre cierra.
Un rechazo del ente deja la venta facturada. Si la anulación vuelve FAILED_FINAL, el comprobante original no se compensó y sigue produciendo efectos fiscales. No es un estado intermedio del que el sistema salga solo: no hay reintento automático.Medido en producción sobre 72 órdenes canceladas: 67 cerraron el circuito, 3 quedaron esperando confirmación y 2 fueron rechazadas. Ese ~7% no se resuelve sin intervención.
Cancelar la orden y anular el comprobante son dos cosas distintas. Tu orden puede quedar cancelada en el acto mientras la anulación fiscal sigue en curso. Si necesitás certeza fiscal —un cierre contable, una conciliación— consultá el documento; el estado de la orden no te la da.

Cuando el instrumento no es una nota de crédito

En Ecuador la anulación produce un documento nuevo. En otros países no: en Brasil es un evento de cancelamento que no genera comprobante, y ahí la respuesta llega con status: "CANCELLED" y document: null. No es un error — es el desenlace correcto de esa operación en ese país. Por eso pedís operation y no un tipo de documento: el instrumento lo decide el régimen.

Consultar una solicitud

Son dos endpoints de lectura, y existen para cuando el camino normal no alcanza: perdiste la respuesta síncrona, o querés ver si el ente ya autorizó sin esperar el evento. En la operación diaria no deberías necesitarlos — los datos llegan por los eventos de la orden.
  • Por identificadorGET /numbering/{fiscalRequestId}.
  • Por ordenGET /numbering?orderCode=…. Devuelve items[], porque una orden puede tener dos documentos: la factura y la anulación que la compensa. Es el que usás cuando perdés la respuesta por un corte de red — el orderCode es lo único que tenés en la mano.
Cuando el ente autoriza, documentStatus pasa a AUTHORIZED.