> ## Documentation Index
> Fetch the complete documentation index at: https://docs.fire.rest/llms.txt
> Use this file to discover all available pages before exploring further.

# Solicitar numeración fiscal

> Obtiene los identificadores fiscales que el punto de venta necesita para imprimir el comprobante. Es síncrono: se llama después de cobrar y antes de inyectar la orden.

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.

<Info>
  **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.
</Info>

## El flujo completo

<Steps>
  <Step title="Cobrás">
    El cliente paga en el POS o el kiosco.
  </Step>

  <Step title="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.
  </Step>

  <Step title="Imprimís">
    Según `printing.mode` imprimís el comprobante fiscal o un ticket provisional.
  </Step>

  <Step title="Inyectás la orden">
    Con el cuerpo de siempre, sin agregarle nada. Fire correlaciona la venta con su
    numeración por el `orderCode`.
  </Step>

  <Step title="El ente autoriza">
    Minutos después. Fire recibe el resultado del proveedor y actualiza la orden. Si querés
    verlo, consultá esta misma solicitud.
  </Step>
</Steps>

<Warning>
  **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.
</Warning>

## Headers

<ParamField header="x-api-key" type="string" required>
  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.
</ParamField>

<ParamField header="Idempotency-Key" type="string" required>
  UUID que identifica **este intento**. Generala una vez por venta y reusala en los reintentos
  de esa misma venta.

  <Note>
    **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.
  </Note>
</ParamField>

<ParamField header="x-correlation-id" type="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`.
</ParamField>

## 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.

<ParamField body="orderCode" type="string" required>
  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.
</ParamField>

<ParamField body="createdAt" type="string" required>
  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`.
</ParamField>

<ParamField body="operation" type="string" default="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.

  <Note>
    **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.
  </Note>
</ParamField>

<ParamField body="store" type="object" required>
  La tienda que emite. Con `code`, Fire resuelve el país, la identidad fiscal del emisor y el
  establecimiento — no los envíes vos.

  <Expandable title="store">
    <ParamField body="code" type="string" required>Código de la tienda en Fire (p. ej. `K004`).</ParamField>
  </Expandable>
</ParamField>

<ParamField body="device" type="object" required>
  El aparato que emite. **Es el mismo bloque que ya mandás en la inyección de órdenes** — no
  hay que agregarle nada.

  <Expandable title="device">
    <ParamField body="uid" type="string" required>
      Identificador del aparato. **Es lo único que identifica al terminal.**
    </ParamField>

    <ParamField body="name" type="string">Nombre del aparato (`KIOSK`, `CAJA 3`).</ParamField>
    <ParamField body="platform" type="string">`android`, `ios`, `web`… Informativo.</ParamField>

    <ParamField body="metadata" type="object">
      Llave-valor del aparato (`ip`, y lo que necesites). Opaco: Fire no lo interpreta.

      Viaja porque cuando una caja emite mal, saber de qué máquina salió es la diferencia
      entre arreglarlo y adivinar.
    </ParamField>
  </Expandable>

  <Note>
    **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.
  </Note>
</ParamField>

<ParamField body="totals" type="array">
  Lo que se cobró. **Es el mismo `payments.totals` que ya mandás en la inyección de órdenes**
  — mandalo tal cual, entero.

  <Expandable title="totals[] — lo que Fire usa para fiscalizar">
    <ParamField body="currencyCode" type="string">
      ISO 4217, el de la tienda: `USD` en Ecuador, `COP` en Colombia, `BRL` en Brasil.
    </ParamField>

    <ParamField body="total" type="number">Total cobrado, impuestos incluidos.</ParamField>
    <ParamField body="subtotalWithoutTaxes" type="number">Base imponible, antes de impuestos.</ParamField>
    <ParamField body="taxValue" type="number">Suma de los impuestos.</ParamField>

    <ParamField body="taxes" type="array">
      Un elemento **por impuesto**, con `name`, `base`, `rate` y `amount`. Siempre el array
      granular: hay regímenes que los declaran por separado y no aceptan el total sumado.

      El nombre es el tuyo (`IVA`, `ICMS`, `PIS`…): traducirlo al código del ente es trabajo
      del proveedor fiscal, no tuyo.
    </ParamField>
  </Expandable>

  <Note>
    **Lo que se manda hoy, por país:**

    | País     | Moneda | Impuestos                                                 |
    | -------- | ------ | --------------------------------------------------------- |
    | Ecuador  | `USD`  | `IVA` 15%                                                 |
    | Colombia | `COP`  | `IVA` 19%                                                 |
    | Brasil   | `BRL`  | seis: `ICMS`, `PIS`, `COFINS`, `IBS_UF`, `IBS_MUN`, `CBS` |

    **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.
  </Note>

  <Note>
    **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`](/es/events/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.
  </Note>

  <Note>
    **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.
  </Note>
</ParamField>

<ParamField body="client" type="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.

  <Expandable title="client — lo que Fire usa para fiscalizar">
    <ParamField body="govIdType" type="string">
      Tipo de documento. Fire espera uno de: **`FINAL_CONSUMER`** · **`CI`** · **`RUC`** ·
      **`CC`** · **`NIT`**.
    </ParamField>

    <ParamField body="govIdNumber" type="string">Número del documento.</ParamField>
    <ParamField body="name" type="string">Nombre. Para empresas, la razón social está en `billingInformation.businessName`.</ParamField>

    <ParamField body="billingInformation" type="object">
      Datos de facturación. Cuando trae `govIdType`/`govIdNumber`, **tienen prioridad** sobre
      los de la raíz: es el documento que el cliente pidió para su factura.
    </ParamField>

    <ParamField body="additionalInfo.fiscal" type="object">
      Domicilio fiscal del comprador, cuando el régimen lo exige para facturar a empresas.
    </ParamField>
  </Expandable>

  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.

  <Note>
    **Los valores que Fire espera en `govIdType`:**

    | Valor            | Qué es                                                    | Dónde    |
    | ---------------- | --------------------------------------------------------- | -------- |
    | `FINAL_CONSUMER` | venta sin comprador identificado — `govIdNumber` en ceros | todos    |
    | `CI`             | cédula de identidad                                       | Ecuador  |
    | `RUC`            | Registro Único de Contribuyentes                          | Ecuador  |
    | `CC`             | cédula de ciudadanía                                      | Colombia |
    | `NIT`            | Número de Identificación Tributaria                       | Colombia |

    **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.
  </Note>

  <Note>
    **Consumidor final: mandá los dos campos, sin traducir ninguno.**

    ```json theme={null}
    "govIdType": "FINAL_CONSUMER",
    "govIdNumber": "00000000000"
    ```

    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.
  </Note>
</ParamField>

<ParamField body="metadata" type="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).
</ParamField>

### 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.

<Info>
  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.
</Info>

<ParamField body="referencedFiscalRequestId" type="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.
</ParamField>

## Respuesta

<ResponseField name="fiscalRequestId" type="string">
  Identificador de la solicitud en Fire. Es con el que consultás el desenlace después.
</ResponseField>

<ResponseField name="orderCode" type="string">Eco del código que enviaste.</ResponseField>

<ResponseField name="correlationId" type="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.
</ResponseField>

<ResponseField name="reused" type="boolean">
  `true` si esta solicitud ya existía y se devolvió tal cual, sin numerar de nuevo.
</ResponseField>

<ResponseField name="requestStatus" type="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.

  | Valor              | Qué pasó                                                       | HTTP  |
  | ------------------ | -------------------------------------------------------------- | ----- |
  | `GENERATED`        | Hay números. Imprimí el comprobante fiscal                     | `201` |
  | `PENDING`          | **No se sabe.** El proveedor no contestó — pudo haber numerado | `202` |
  | `FAILED_RETRYABLE` | El proveedor dijo "no ahora". Podés reintentar (hasta 2 veces) | `202` |
  | `FAILED_FINAL`     | El proveedor dijo "no" definitivo. Reintentar no sirve         | `200` |
  | `NOT_APPLICABLE`   | Esta tienda no tiene numeración fiscal. **No es un error**     | `200` |

  <Warning>
    **`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.
  </Warning>

  <Note>
    `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.
  </Note>
</ResponseField>

<ResponseField name="documentStatus" type="string">
  Estado del **documento ante el ente**: ¿lo autorizó?

  `PENDING` · `AUTHORIZED` · `REJECTED` · `CANCELLED`

  <Note>
    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.
  </Note>
</ResponseField>

<ResponseField name="environment" type="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.

  <Warning>
    **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.
  </Warning>

  `null` cuando `requestStatus` es `NOT_APPLICABLE`: no se numeró, así que no hubo ambiente
  en el que numerar.
</ResponseField>

<ResponseField name="document" type="object">
  Lo que necesitás para imprimir, **sin saber de países**. `null` si no se numeró.

  <Expandable title="document">
    <ResponseField name="documentType" type="string">
      `SALE_INVOICE` o `CREDIT_NOTE`. Vocabulario de Fire: dice qué operación es, no con qué
      instrumento la materializa el país.
    </ResponseField>

    <ResponseField name="documentLabel" type="string">
      **Cómo se titula en el comprobante**: `FACTURA`, `NOTA DE CREDITO`. Lo traduce Fire —
      el instrumento fiscal lo define el régimen, y no queremos que cada canal lleve su mapa.
    </ResponseField>

    <ResponseField name="documentNumber" type="string">
      Número visible, **tal como lo arma el proveedor** según la convención de su país
      (`001-020-000000123`). Es para **imprimir**: para buscar o conciliar usá los
      identificadores de `countryData`.

      En Ecuador es el eco de `countryData.numeroComprobante`. Fire no lo recompone ni le
      cambia el formato — la regla es del régimen, no nuestra.
    </ResponseField>

    <ResponseField name="authorizationMode" type="string">
      `ONLINE` · `OFFLINE` · `BATCH`. Vocabulario de Fire.
    </ResponseField>

    <ResponseField name="authorizationLabel" type="string">
      **Cómo se imprime**: `EMISION NORMAL`, `EMISION POR CONTINGENCIA`. Misma razón que
      `documentLabel`.
    </ResponseField>

    <ResponseField name="issuedAt" type="string">Fecha de emisión.</ResponseField>

    <ResponseField name="compensates" type="object">
      **Qué documento anula este.** Solo en notas de crédito; `null` en una factura.

      ```json theme={null}
      {
        "documentNumber": "005-004-000000068",
        "issuedAt": "2026-08-14T18:31:57.649Z",
        "reason": "ORDER_CANCELLATION",
        "reasonLabel": "Anulación de pedido"
      }
      ```

      <Expandable title="compensates">
        <ResponseField name="documentNumber" type="string">
          Número visible del documento original. En Ecuador se imprime como
          `N. FACTURA MODIFICADA`.
        </ResponseField>

        <ResponseField name="issuedAt" type="string">
          Cuándo se emitió el original. Se imprime como `FECHA EMISION FAC.` — es distinta de
          la fecha de la nota de crédito, que está un nivel arriba.
        </ResponseField>

        <ResponseField name="reason" type="string">
          Por qué se anula. Vocabulario de Fire. Hoy solo existe `ORDER_CANCELLATION`: la
          anulación del pedido completo.
        </ResponseField>

        <ResponseField name="reasonLabel" type="string">
          Cómo se imprime el motivo. **Lo redacta cada empresa** en su configuración: el ente
          exige que la nota de crédito lleve un motivo, pero no dicta el texto.
        </ResponseField>
      </Expandable>

      Es un bloque **universal**: toda anulación, en cualquier país, referencia el documento
      que modifica. Lo que cambia por país es cómo se rotula al imprimirlo, no el concepto —
      por eso vive acá y no en `countryData`.
    </ResponseField>
  </Expandable>

  <Note>
    **`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.
  </Note>
</ResponseField>

<ResponseField name="countryData" type="object">
  **Los identificadores del país, en el vocabulario de su ente y listos para imprimir.**

  <CodeGroup>
    ```json Ecuador (EC) — SRI theme={null}
    {
      "numeroComprobante": "001-020-000000123",
      "claveAcceso": "1208202601179141513200110010200000001231234567813",
      "establecimiento": "001",
      "puntoEmision": "020",
      "secuencial": "000000123",
      "ambiente": "PRODUCCION"
    }
    ```

    ```json Colombia (CO) — DIAN theme={null}
    {
      "numeroComprobante": "SETP990000001",
      "cufe": "9c4f1e… (96)",
      "prefijo": "SETP",
      "numeroDian": "990000001",
      "qrCode": "https://catalogo-vpfe.dian.gov.co/document/searchqr?documentkey=9c4f1e…",
      "ambiente": "PRODUCCION"
    }
    ```
  </CodeGroup>

  <Note>
    **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.
  </Note>

  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.

  <Note>
    **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.
  </Note>

  <Warning>
    **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.
  </Warning>
</ResponseField>

### `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.

<Tabs>
  <Tab title="Ecuador (EC) · disponible">
    Comprobantes del **SRI**.

    | Clave               | Tipo   | Siempre | Notas                                                                            |
    | ------------------- | ------ | ------- | -------------------------------------------------------------------------------- |
    | `numeroComprobante` | string | ✓       | El número **visible**, ya armado: `estab-ptoEmi-secuencial`                      |
    | `claveAcceso`       | string | ✓       | 49 dígitos. Es también lo que se codifica en el QR                               |
    | `establecimiento`   | string | ✓       | 3 dígitos. Lo resuelve el proveedor desde `store.code`                           |
    | `puntoEmision`      | string | ✓       | 3 dígitos. Lo resuelve el proveedor desde `device.uid`                           |
    | `secuencial`        | string | ✓       | 9 dígitos. La factura y la nota de crédito llevan **secuencias distintas**       |
    | `ambiente`          | string | ✓       | `PRUEBAS` o `PRODUCCION` — **ya traducido**; el SRI lo define como `"1"` / `"2"` |

    ```json theme={null}
    {
      "numeroComprobante": "001-020-000000123",
      "claveAcceso": "1208202601179141513200110010200000001231234567813",
      "establecimiento": "001",
      "puntoEmision": "020",
      "secuencial": "000000123",
      "ambiente": "PRODUCCION"
    }
    ```

    <Note>
      **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.
    </Note>

    <Warning>
      **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.
    </Warning>
  </Tab>

  <Tab title="Colombia (CO) · disponible">
    Comprobantes de la **DIAN**.

    | Clave               | Tipo   | Siempre | Notas                                                                                 |
    | ------------------- | ------ | ------- | ------------------------------------------------------------------------------------- |
    | `numeroComprobante` | string | ✓       | El número **visible**, ya armado: `prefijo + consecutivo`                             |
    | `cufe`              | string | ✓       | 96 caracteres hexadecimales (SHA-384). Es el identificador del documento ante la DIAN |
    | `prefijo`           | string | ✓       | Prefijo del rango de numeración autorizado por resolución                             |
    | `numeroDian`        | string | ✓       | El consecutivo **solo**, sin el prefijo                                               |
    | `qrCode`            | string | ✓       | URL del catálogo de la DIAN. Es lo que se imprime como QR                             |
    | `ambiente`          | string | ✓       | `PRUEBAS` o `PRODUCCION` — **ya traducido**; la DIAN lo define como `"1"` / `"2"`     |

    ```json theme={null}
    {
      "numeroComprobante": "SETP990000001",
      "cufe": "9c4f1e… (96 caracteres hexadecimales)",
      "prefijo": "SETP",
      "numeroDian": "990000001",
      "qrCode": "https://catalogo-vpfe.dian.gov.co/document/searchqr?documentkey=9c4f1e…",
      "ambiente": "PRODUCCION"
    }
    ```

    <Warning>
      **`ambiente` llega traducido, y menos mal.** La DIAN usa `1` para producción y `2` para
      pruebas — **al revés que el SRI**. Fire lo resuelve acá para que ningún canal tenga que
      llevar su propia copia de la tabla: el primero que la copie del lado equivocado imprime
      "PRUEBAS" en una factura real.
    </Warning>

    <Note>
      **`numeroComprobante` y `numeroDian` no son lo mismo.** El primero es el número visible
      completo, tal como va impreso; el segundo es solo el consecutivo. Los dos llegan
      resueltos por el proveedor — Fire no concatena nada, igual que en Ecuador.

      `document.documentNumber` es un **eco** de `numeroComprobante`, para imprimir sin entrar
      al bloque del país.
    </Note>

    <Warning>
      **No hay `graphic` en Colombia.** El QR es la URL del catálogo de la DIAN y vive en
      `countryData.qrCode`. Un canal que espere `graphic.qr` como en Ecuador no encuentra nada.
    </Warning>

    <Note>
      **Solo CUFE, no CUDE.** Emitimos factura electrónica de venta, que lleva CUFE. El
      "documento equivalente P.O.S." lleva CUDE y hoy no se emite.
    </Note>
  </Tab>

  <Tab title="Otros países · cuando entren">
    Un país entra con su adaptador, y con él llegan sus claves y su pestaña. La forma de la
    respuesta **no cambia**: `countryData` sigue siendo el mismo mapa abierto.

    Lo que sí cambia es el vocabulario, y por eso no conviene indexar claves fijas: Ecuador
    habla de `claveAcceso` y Colombia de `cufe`, para el mismo hecho. Brasil dirá
    `chaveAcesso`. Son ejemplos de cómo nombra cada régimen, no un contrato ya disponible.

    <Warning>
      **Si tu integración opera en más de un país, recorré las claves.** Un canal que lea
      `countryData.claveAcceso` directo funciona en Ecuador y ya se rompe en Colombia.
    </Warning>
  </Tab>
</Tabs>

<ResponseField name="store" type="object">
  La **sucursal** que emite, para el encabezado del comprobante.

  <Expandable title="store">
    <ResponseField name="code" type="string">Código de tienda del negocio.</ResponseField>
    <ResponseField name="name" type="string">Nombre de la tienda.</ResponseField>

    <ResponseField name="address" type="string">
      Domicilio de la sucursal. **No es el de la matriz** — el comprobante ecuatoriano imprime
      los dos, y son distintos.
    </ResponseField>

    <ResponseField name="city" type="string">Ciudad.</ResponseField>
    <ResponseField name="phone" type="string">Teléfono.</ResponseField>
    <ResponseField name="govIdType" type="string">Tipo de identificación fiscal de la sucursal.</ResponseField>
    <ResponseField name="govIdNumber" type="string">Identificación fiscal de la sucursal.</ResponseField>
    <ResponseField name="secondaryGovIdType" type="string">Identificación secundaria, si aplica.</ResponseField>
    <ResponseField name="secondaryGovIdNumber" type="string">Valor de la anterior.</ResponseField>
  </Expandable>
</ResponseField>

<ResponseField name="company" type="object">
  La **persona jurídica** que emite: el encabezado y el pie del comprobante, ya resueltos.

  <Expandable title="company">
    <ResponseField name="legalName" type="string">Razón social.</ResponseField>
    <ResponseField name="tradeName" type="string">Nombre comercial.</ResponseField>
    <ResponseField name="govIdType" type="string">Tipo de identificación (`RUC`, `CNPJ`…).</ResponseField>
    <ResponseField name="govIdNumber" type="string">Identificación de la empresa.</ResponseField>

    <ResponseField name="headquartersAddress" type="string">
      Domicilio de la **matriz**, distinto del de la sucursal.
    </ResponseField>

    <ResponseField name="countryLines" type="array">
      Lo que el régimen exige en el encabezado, **ya rotulado y ordenado**:

      ```json theme={null}
      [
        { "key": "granContribuyente", "label": "GRAN CONTRIBUYENTE", "value": "NAC-GCFOIOC21-00000900-E" },
        { "key": "contribuyenteEspecial", "label": "CONTRIBUYENTE ESPECIAL", "value": "155" },
        { "key": "obligadoContabilidad", "label": "Obligado a llevar contabilidad", "value": "SI" }
      ]
      ```

      Viene con `label` porque el comprobante lo imprime literal. Si el rótulo lo pusiera cada
      canal, dos cajas de la misma marca imprimirían distinto. **Iterá la lista y dibujá**: no
      necesitás saber qué significa "gran contribuyente", solo dónde ponerlo.
    </ResponseField>

    <ResponseField name="legends" type="array">
      Los textos del pie, **en orden y ya interpolados**:

      ```json theme={null}
      [
        { "key": "avisoCambios", "text": "Estimado cliente: Por favor verifique los datos…" },
        { "key": "facturaElectronica", "text": "…con la Clave de Acceso: 1408…7811" }
      ]
      ```

      La `key` es estable y la elige quien los carga: si preferís tus propios textos, indexá
      por ella e ignorá el nuestro.

      Una leyenda que interpola un dato que falta **no viaja**: media leyenda con un
      marcador crudo impreso es peor que no imprimirla.
    </ResponseField>

    <Warning>
      **Sin comprobante no viajan `countryLines` ni `legends`.** Son atributos del
      comprobante, no de la empresa: el ente los exige *en* la factura. Cuando la numeración
      no produce documento —`PENDING`, `FAILED_RETRYABLE`, `FAILED_FINAL`— los dos llegan
      como `[]`.

      La **identidad** sí llega completa (`legalName`, `tradeName`, `govIdType`,
      `govIdNumber`, `headquartersAddress`): el ticket provisional necesita encabezado con
      quién vendió.

      Sin esta regla, un provisional imprimía "verifique los datos de su factura, únicamente
      se aceptarán cambios el mismo día de emisión" sobre un papel que **no es una factura**,
      y declaraba un "GRAN CONTRIBUYENTE" en un documento que no declara nada.
    </Warning>
  </Expandable>

  <Note>
    **`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](/es/api-reference/fiscal-print).
  </Note>
</ResponseField>

<ResponseField name="graphic" type="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.
</ResponseField>

<ResponseField name="printing" type="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.

  <Expandable title="printing">
    <ResponseField name="printable" type="boolean">Si podés entregar el comprobante fiscal.</ResponseField>

    <ResponseField name="mode" type="string">
      **Qué papel sale de la impresora.** Tres valores, cerrados.

      | valor                 | qué imprimís                           | cuándo                                                        |
      | --------------------- | -------------------------------------- | ------------------------------------------------------------- |
      | `FISCAL_DOCUMENT`     | El comprobante fiscal, con sus números | Hay numeración (`GENERATED`) y el país deja entregarlo        |
      | `PROVISIONAL_RECEIPT` | Un ticket **no fiscal**                | No hay numeración todavía, o el ente rechazó                  |
      | `NONE`                | Nada                                   | Esta tienda no tiene representación fiscal (`NOT_APPLICABLE`) |
    </ResponseField>

    <ResponseField name="reason" type="string">
      **Por qué ese modo**, para que puedas explicárselo al cajero. `null` cuando `mode` es
      `FISCAL_DOCUMENT` — no hace falta justificar el caso normal.

      | valor                       | qué pasó                                                  |
      | --------------------------- | --------------------------------------------------------- |
      | `ISSUED_OFFLINE`            | El país permite emitir sin conexión y regularizar después |
      | `AWAITING_FISCAL_NUMBERING` | Todavía no hay números: se pidieron y no llegaron         |
      | `FISCAL_REJECTED`           | El ente o el proveedor dijeron que no                     |
      | `NUMBERING_DISABLED`        | Este vendor no numera — no es un error                    |
    </ResponseField>
  </Expandable>
</ResponseField>

<ResponseField name="policy" type="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.

  <Note>
    **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.
  </Note>

  <Expandable title="policy.numberingFailure">
    <ResponseField name="action" type="string">
      **Lo único que tenés que leer.** Enum cerrado de dos valores, y no van a crecer sin aviso.

      | valor      | qué hacés                                                                     | qué NO hacés             |
      | ---------- | ----------------------------------------------------------------------------- | ------------------------ |
      | `CONTINUE` | Imprimís según `printing.mode` e inyectás la orden                            | No devolvés plata        |
      | `REFUND`   | Devolvés el cobro y [reportás la venta perdida](/es/api-reference/lost-sales) | **No inyectás la orden** |

      `CONTINUE` es el default: es lo que sale sin configurar, con la configuración rota, y en
      todo caso donde no hubo fallo.

      `CONTINUE` → seguí como siempre: imprimí según `printing.mode` e inyectá la orden.

      `REFUND` → devolvé el cobro en el mostrador y **no inyectes la orden**. Después reportala
      con [Registrar venta perdida](/es/api-reference/lost-sales).

      No existe un valor para "bloqueá la venta": cuando pedís la numeración el cliente **ya
      pagó**. No hay venta que bloquear — lo único que se puede decidir es si le devolvés la
      plata.
    </ResponseField>

    <ResponseField name="lostSaleReason" type="string">
      Con qué `reason` reportar esa venta. Copialo tal cual — así no tenés que conocer nuestro
      catálogo, y el día que agreguemos una causa no tocás código.

      **Es un enum cerrado, y hoy tiene un solo valor:**

      | valor                     | qué pasó                               |
      | ------------------------- | -------------------------------------- |
      | `FISCAL_NUMBERING_FAILED` | La venta se cobró y no se pudo numerar |

      Este campo **es** el `reason` de
      [Registrar venta perdida](/es/api-reference/lost-sales): lo pasás sin transformarlo. No hay
      endpoint para consultar el catálogo, y a propósito — mientras sea un enum de este tamaño,
      pedirte una llamada de más para descubrir un valor que ya te estamos mandando en esta
      respuesta sería trabajo sin beneficio. Si algún día crece lo suficiente como para que
      valga la pena, el catálogo pasa a ser un endpoint y este campo no cambia.

      Por eso mismo: **ramificá por el valor sólo si tenés que hacer algo distinto según la
      causa.** Para reportar, copiá. Un canal que hoy hardcodea `FISCAL_NUMBERING_FAILED` en vez
      de leerlo de acá funciona igual —hay un solo valor— y se rompe en silencio el día que
      haya dos.

      `null` cuando esa venta **no puede terminar sin orden**: la numeración salió bien, o lo
      que falló era una anulación —ahí la orden existe y su dinero no se devolvió—.
    </ResponseField>

    <ResponseField name="configVersion" type="string">
      Huella de la configuración con la que se decidió, tipo `fnv1a:d096701f`. Sirve para lo
      mismo que el hash de un deploy: agarrás una devolución de hace tres semanas y sabés con
      qué configuración se decidió, aunque hoy sea otra.

      **`null` significa que la cuenta no configuró nada** y se aplicó el default.
    </ResponseField>

    <ResponseField name="resolvedAt" type="string">Cuándo se resolvió.</ResponseField>

    <ResponseField name="resolvedFrom" type="object">
      El contexto **evaluado**: `requestStatus`, `operation`, `retryable`, y —si hubo respuesta
      del proveedor— `failureCode` y `failureScope`.

      Es un recibo forense, no la regla. **Ninguno de estos campos decide nada**: la decisión
      sale de lo que configuró la cuenta. Están para poder reconstruir por qué se decidió eso
      aunque después cambien la configuración.
    </ResponseField>
  </Expandable>
</ResponseField>

<Note>
  **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`.
</Note>

<Warning>
  **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.
</Warning>

### Qué hacer cuando llega `REFUND`

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

<Steps>
  <Step title="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.
  </Step>

  <Step title="No inyectes la orden">
    No la mandes a [Crear orden](/es/api-reference/orders). Esa venta no ocurrió: inyectarla
    dejaría una orden cobrada sin comprobante fiscal, que es peor que no tenerla.
  </Step>

  <Step title="Reportala con Registrar venta perdida">
    `POST /orders/lost-sales`, copiando `policy.numberingFailure.lostSaleReason` en `reason`.
    Es el único paso que nos avisa a nosotros.
  </Step>
</Steps>

<Warning>
  **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á.
</Warning>

### 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](/es/api-reference/cancel-order).

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.

<Warning>
  **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**.
</Warning>

<Warning>
  **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](/es/api-reference/cancel-order)
  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.
</Warning>

<ResponseField name="failure" type="object">
  Por qué no hay documento. `null` cuando sí lo hay.

  <Expandable title="failure">
    <ResponseField name="scope" type="string">
      **Lo que ramificás.** `TECHNICAL` → el problema es de comunicación o del servicio;
      imprimís provisional y se resuelve después. `FUNCTIONAL` → hay un dato mal y reintentar
      no lo arregla.
    </ResponseField>

    <ResponseField name="code" type="string">
      Código estable, para alertas y soporte. **No es un estado**: los estados son los cinco de
      `requestStatus` y no crecen; este catálogo sí.
    </ResponseField>

    <ResponseField name="message" type="string">
      Texto accionable. Dice qué tienda, qué punto de emisión o qué dato falta.

      **Cuando el proveedor manda un motivo, es el suyo, literal** — por ejemplo
      `"clave de API inválida"`. Sólo si no manda ninguno usamos un texto propio según el
      `code`. Es un texto **para leer, no para ramificar**: viene del proveedor y puede
      cambiar sin aviso. Para decidir, usá `code` y `scope`.
    </ResponseField>
  </Expandable>

  | `code`                         | Qué pasó                                                                                        | `scope`      |
  | ------------------------------ | ----------------------------------------------------------------------------------------------- | ------------ |
  | `FISCAL_BUSINESS_RULE`         | El ente o el proveedor rechazaron por una regla de negocio. El `message` trae el motivo real    | `FUNCTIONAL` |
  | `FISCAL_COUNTRY_NOT_SUPPORTED` | El proveedor no atiende el país de esa tienda                                                   | `TECHNICAL`  |
  | `PROVIDER_AUTH_FAILED`         | Credencial del proveedor equivocada o revocada. **Es configuración, no un fallo de numeración** | `TECHNICAL`  |
  | `PROVIDER_TIMEOUT`             | No contestó dentro del presupuesto de tiempo                                                    | `TECHNICAL`  |
  | `PROVIDER_UNAVAILABLE`         | Contestó que no puede ahora                                                                     | `TECHNICAL`  |
  | `PROVIDER_UNREACHABLE`         | No se pudo establecer comunicación                                                              | `TECHNICAL`  |
  | `PROVIDER_CONTRACT_VIOLATION`  | Contestó `2xx` con algo que no cumple el contrato                                               | `TECHNICAL`  |

  <Warning>
    **`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.
  </Warning>
</ResponseField>

<ResponseField name="providerCode" type="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.
</ResponseField>

<ResponseField name="providerIdentity" type="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ó.

  <Expandable title="providerIdentity">
    <ResponseField name="name" type="string">
      Identificador estable del servicio que resolvió la numeración.
    </ResponseField>

    <ResponseField name="version" type="string">
      Qué versión la resolvió. Es lo que permite acotar un problema a un despliegue.
    </ResponseField>

    <ResponseField name="reference" type="string">
      **La referencia de soporte del proveedor**: el identificador que le citás a él para que
      encuentre esta operación en sus propios registros. **No es tu `Idempotency-Key`** — esa
      la mandaste vos y vuelve en `idempotencyKey`.
    </ResponseField>
  </Expandable>
</ResponseField>

<ResponseField name="providerMetadata" type="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.

  <Note>
    **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.
  </Note>
</ResponseField>

## Códigos de estado

<Warning>
  **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.
</Warning>

| Código        | Cuándo                                                                          | `requestStatus`                |
| ------------- | ------------------------------------------------------------------------------- | ------------------------------ |
| `201`         | Numerado ahora                                                                  | `GENERATED`                    |
| `200`         | Reintento idempotente — ya existía (`reused: true`)                             | cualquiera                     |
| `200`         | Esta tienda no numera. **No es un error**                                       | `NOT_APPLICABLE`               |
| `200`         | Rechazo definitivo. Es la respuesta a tu pregunta, no una falla de la llamada   | `FAILED_FINAL`                 |
| `202`         | No hay números **todavía**. Imprimí provisional e inyectá igual                 | `PENDING` · `FAILED_RETRYABLE` |
| `400`         | Cuerpo inválido o tienda sin configurar para emitir                             | —                              |
| `401` · `403` | Credenciales o permiso                                                          | —                              |
| `404`         | La tienda no existe para el vendor de tu API key                                | —                              |
| `409`         | Misma `Idempotency-Key` con otro cuerpo, o el `orderCode` ya lo usó otra tienda | —                              |

<Note>
  **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.
</Note>

## Qué hacer con cada respuesta

<Note>
  **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](/es/api-reference/lost-sales).

  Lo demás de cada estado —qué significa y si el fallo se arregla solo— vale en los dos casos.
</Note>

<AccordionGroup>
  <Accordion title="GENERATED — hay comprobante" icon="circle-check">
    `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.

    <CodeGroup>
      ```json Ecuador (EC) — SRI theme={null}
      {
        "requestStatus": "GENERATED",
        "documentStatus": "PENDING",
        "reused": false,
        "document": {
          "documentType": "SALE_INVOICE",
          "documentLabel": "FACTURA",
          "documentNumber": "005-004-000000058",
          "authorizationMode": "ONLINE",
          "authorizationLabel": "EMISION NORMAL",
          "issuedAt": "2026-08-14T01:01:14.722Z"
        },
        "countryData": {
          "numeroComprobante": "005-004-000000058",
          "claveAcceso": "1308202601000000000000210050040000000581234567811",
          "establecimiento": "005",
          "puntoEmision": "004",
          "secuencial": "000000058",
          "ambiente": "PRODUCCION"
        },
        "printing": { "printable": true, "mode": "FISCAL_DOCUMENT", "reason": null },
        "graphic": { "qr": "1308202601000000000000210050040000000581234567811" },
        "failure": null
      }
      ```

      ```json Colombia (CO) — DIAN theme={null}
      {
        "requestStatus": "GENERATED",
        "documentStatus": "PENDING",
        "reused": false,
        "document": {
          "documentType": "SALE_INVOICE",
          "documentLabel": "FACTURA ELECTRONICA DE VENTA",
          "documentNumber": "SETP990000001",
          "authorizationMode": "ONLINE",
          "authorizationLabel": "VALIDACION PREVIA",
          "issuedAt": "2026-08-16T14:21:03.118Z"
        },
        "countryData": {
          "numeroComprobante": "SETP990000001",
          "cufe": "9c4f1e… (96 hex)",
          "prefijo": "SETP",
          "numeroDian": "990000001",
          "qrCode": "https://catalogo-vpfe.dian.gov.co/document/searchqr?documentkey=9c4f1e…",
          "ambiente": "PRODUCCION"
        },
        "printing": { "printable": true, "mode": "FISCAL_DOCUMENT", "reason": null },
        "graphic": null,
        "failure": null
      }
      ```
    </CodeGroup>
  </Accordion>

  <Accordion title="PENDING — no se sabe si hay comprobante" icon="circle-question">
    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.

    <Warning>
      **No asumas que la venta quedó sin comprobante.** Emitir uno nuevo por otro camino puede
      declarar la misma venta dos veces ante el ente.
    </Warning>

    ```json theme={null}
    {
      "requestStatus": "PENDING",
      "document": null,
      "printing": { "printable": false, "mode": "PROVISIONAL_RECEIPT", "reason": "AWAITING_FISCAL_NUMBERING" },
      "graphic": null,
      "retryable": true,
      "environment": "PRODUCTION",
      "failure": {
        "code": "PROVIDER_TIMEOUT",
        "scope": "TECHNICAL",
        "message": "El proveedor fiscal no respondió dentro del tiempo configurado."
      }
    }
    ```
  </Accordion>

  <Accordion title="FAILED_RETRYABLE — no hay, pero puede haber" icon="rotate-right">
    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`.

    ```json theme={null}
    {
      "requestStatus": "FAILED_RETRYABLE",
      "document": null,
      "printing": { "printable": false, "mode": "PROVISIONAL_RECEIPT", "reason": "AWAITING_FISCAL_NUMBERING" },
      "graphic": null,
      "retryable": true,
      "environment": "PRODUCTION",
      "failure": {
        "code": "PROVIDER_UNAVAILABLE",
        "scope": "TECHNICAL",
        "message": "El servicio de fiscalización no está disponible."
      }
    }
    ```
  </Accordion>

  <Accordion title="FAILED_FINAL — no hay, y reintentar no sirve" icon="circle-xmark">
    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.

    <CodeGroup>
      ```json FUNCTIONAL — un dato no se acepta theme={null}
      {
        "requestStatus": "FAILED_FINAL",
        "document": null,
        "printing": { "printable": false, "mode": "PROVISIONAL_RECEIPT", "reason": "FISCAL_REJECTED" },
        "graphic": null,
        "retryable": false,
        "environment": "PRODUCTION",
        "failure": {
          "code": "FISCAL_BUSINESS_RULE",
          "scope": "FUNCTIONAL",
          "message": "identidad fiscal no configurada: EC / la tienda K004 no está cargada en el catálogo de identidades fiscales"
        }
      }
      ```

      ```json TECHNICAL — el proveedor rechazó las credenciales theme={null}
      {
        "requestStatus": "FAILED_FINAL",
        "document": null,
        "printing": { "printable": false, "mode": "PROVISIONAL_RECEIPT", "reason": "FISCAL_REJECTED" },
        "graphic": null,
        "retryable": false,
        "environment": "SANDBOX",
        "failure": {
          "code": "PROVIDER_AUTH_FAILED",
          "scope": "TECHNICAL",
          "message": "clave de API inválida"
        },
        "company": {
          "legalName": "INT FOOD SERVICES CORP S.A.",
          "tradeName": "KFC",
          "govIdType": "RUC",
          "govIdNumber": "1791415132001",
          "headquartersAddress": "PICHINCHA / QUITO / COREA 126 Y AV. AMAZONAS",
          "countryLines": [],
          "legends": []
        },
        "timestamps": {
          "requestedAt": "2026-08-18T21:36:58.702Z",
          "respondedAt": "2026-08-18T21:36:59.617Z",
          "providerLatencyMs": 742
        }
      }
      ```
    </CodeGroup>

    <Warning>
      **`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**.
    </Warning>
  </Accordion>

  <Accordion title="NOT_APPLICABLE — esta tienda no numera" icon="ban">
    **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.

    ```json theme={null}
    {
      "fiscalRequestId": null,
      "requestStatus": "NOT_APPLICABLE",
      "document": null,
      "printing": { "printable": false, "mode": "NONE", "reason": "NUMBERING_DISABLED" },
      "graphic": null,
      "failure": null,
      "documentStatus": null,
      "idempotencyKey": null,
      "store": {
        "code": "K000",
        "name": "Laboratorio Ecuador",
        "address": "PICHINCHA / QUITO / AV. AMAZONAS Y AV COREA",
        "city": "Quito",
        "phone": "023920070",
        "govIdType": "RUC",
        "govIdNumber": "1791415132001"
      },
      "company": {
        "legalName": "INT FOOD SERVICES CORP S.A.",
        "tradeName": "KFC",
        "govIdType": "RUC",
        "govIdNumber": "1791415132001",
        "countryLines": [],
        "legends": []
      }
    }
    ```

    <Note>
      **`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.
    </Note>
  </Accordion>

  <Accordion title="4xx — el problema está en la petición">
    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.

    ```json theme={null}
    {
      "success": false,
      "error": "VALIDATION_ERROR",
      "message": "La tienda no tiene identificador tributario del emisor configurado (settings.fiscal.govIdNumber)",
      "data": {
        "storeCode": "K000",
        "store": { "code": "K000", "name": "Laboratorio Ecuador", "address": "…" },
        "company": { "legalName": "INT FOOD SERVICES CORP S.A.", "countryLines": [], "legends": [] }
      }
    }
    ```
  </Accordion>
</AccordionGroup>

<Info>
  **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.
</Info>

## 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.

```json theme={null}
{
  "orderCode": "EC-K004-42-1786579046934",
  "createdAt": "2026-08-12T19:10:00.000Z",
  "operation": "CANCEL",
  "store": { "code": "K004" },
  "device": { "uid": "kiosk-01", "name": "KIOSK", "platform": "android" },
  "metadata": {}
}
```

La respuesta tiene **la misma forma** que la de una factura. Cambian tres cosas:

|                          | Factura        | Anulación              |
| ------------------------ | -------------- | ---------------------- |
| `document.documentType`  | `SALE_INVOICE` | `CREDIT_NOTE`          |
| `document.documentLabel` | `FACTURA`      | `NOTA DE CREDITO RIDE` |
| `document.compensates`   | `null`         | el documento que anula |

<Note>
  **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.
</Note>

<Warning>
  **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.
</Warning>

### 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`**:

```json theme={null}
{
  "success": false,
  "error": "VALIDATION_ERROR",
  "message": "No existe un documento emitido para esa orden al que referenciar la nota de crédito"
}
```

<Warning>
  **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.
</Warning>

### 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.

| `requestStatus`    | Qué pasó                                 | Qué hacer                             |
| ------------------ | ---------------------------------------- | ------------------------------------- |
| `GENERATED`        | La anulación quedó numerada              | Nada                                  |
| `PENDING`          | **No se sabe.** El proveedor no contestó | Consultar por `orderCode`             |
| `FAILED_RETRYABLE` | Falló y se puede reintentar              | Reintentar con el mismo `orderCode`   |
| `FAILED_FINAL`     | El ente rechazó la anulación             | **La factura original sigue vigente** |

<Warning>
  **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.
</Warning>

<Note>
  **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.
</Note>

### 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 identificador](/es/api-reference/fiscal-document-get)** — `GET /numbering/{fiscalRequestId}`.
* **[Por orden](/es/api-reference/fiscal-documents-query)** — `GET /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`.

<RequestExample>
  ```json Facturar theme={null}
  {
    "orderCode": "EC-K004-42-1786579046934",
    "createdAt": "2026-08-12T17:26:09.386Z",
    "operation": "INVOICE",
    "store": { "code": "K004" },
    "device": { "uid": "kiosk-01", "name": "KIOSK", "platform": "android" },
    "totals": [
      {
        "currencyCode": "USD",
        "total": 10,
        "subtotalWithoutTaxes": 8.7,
        "taxValue": 1.3,
        "taxes": [
          { "name": "IVA", "base": 8.7, "rate": "0.15", "amount": 1.3 }
        ]
      }
    ],
    "client": {
      "uid": "8Z35YvBbgKVj67AJwZ3nFmAqrtk1",
      "name": "CONSUMIDOR",
      "lastName": "FINAL",
      "email": "consumidor.final@kfc.com.ec",
      "phone": "2222222",
      "govIdType": "FINAL_CONSUMER",
      "govIdNumber": "00000000000",
      "externalId": "",
      "additionalInfo": {
        "fiscal": "",
        "gender": "",
        "birthdate": ""
      },
      "billingInformation": {
        "email": "",
        "phone": "2222222",
        "address": "",
        "govIdType": "FINAL_CONSUMER",
        "externalId": "",
        "govIdNumber": "00000000000",
        "businessName": ""
      }
    },
    "metadata": {}
  }
  ```

  ```json Facturar — Colombia (CO) theme={null}
  {
    "orderCode": "CO-K039-1786901234",
    "createdAt": "2026-08-16T14:03:22.145Z",
    "operation": "INVOICE",
    "store": { "code": "K039" },
    "device": { "uid": "52CAEA5A18D9B75F", "name": "CAJA 3", "platform": "android" },
    "client": {
      "name": "Consumidor",
      "lastName": "final",
      "govIdType": "FINAL_CONSUMER",
      "govIdNumber": "00000000000"
    },
    "totals": [
      {
        "currencyCode": "COP",
        "total": 50000,
        "subtotalWithoutTaxes": 42016.81,
        "taxValue": 7983.19,
        "taxes": [
          { "name": "IVA", "base": 42016.81, "rate": "0.19", "amount": 7983.19 }
        ]
      }
    ],
    "metadata": {}
  }
  ```

  ```json Anular theme={null}
  {
    "orderCode": "EC-K004-42-1786579046934",
    "createdAt": "2026-08-12T19:10:00.000Z",
    "operation": "CANCEL",
    "store": { "code": "K004" },
    "device": { "uid": "kiosk-01", "name": "KIOSK", "platform": "android" },
    "client": {
      "uid": "8Z35YvBbgKVj67AJwZ3nFmAqrtk1",
      "name": "CONSUMIDOR",
      "lastName": "FINAL",
      "email": "consumidor.final@ejemplo.com",
      "phone": "2222222",
      "govIdType": "FINAL_CONSUMER",
      "govIdNumber": "00000000000",
      "externalId": "",
      "additionalInfo": { "fiscal": "", "gender": "", "birthdate": "" },
      "billingInformation": {
        "email": "", "phone": "2222222", "address": "",
        "govIdType": "FINAL_CONSUMER", "externalId": "",
        "govIdNumber": "00000000000", "businessName": ""
      }
    },
    "totals": [
      {
        "currencyCode": "USD",
        "total": 10,
        "subtotalWithoutTaxes": 8.7,
        "taxValue": 1.3,
        "taxes": [
          { "name": "IVA", "base": 8.7, "rate": "0.15", "amount": 1.3 }
        ]
      }
    ],
    "metadata": {}
  }
  ```
</RequestExample>

<ResponseExample>
  ```json 201 Numerado theme={null}
  {
    "success": true,
    "data": {
      "fiscalRequestId": "8d5b425c-87d2-4848-ab74-517a0ca2743e",
      "orderCode": "EC-K004-42-1786579046934",
      "idempotencyKey": "9305c960-2304-4b37-a845-fea3a38daa5e",
      "reused": false,
      "countryCode": "EC",
      "requestStatus": "GENERATED",
      "documentStatus": "PENDING",
      "retryable": false,
      "environment": "PRODUCTION",
      "document": {
        "documentType": "SALE_INVOICE",
        "documentLabel": "FACTURA",
        "documentNumber": "005-004-000000011",
        "authorizationMode": "ONLINE",
        "authorizationLabel": "EMISION NORMAL",
        "issuedAt": "2026-08-12T23:57:47.118Z",
        "compensates": null
      },
      "countryData": {
        "numeroComprobante": "005-004-000000011",
        "claveAcceso": "1208202601000000000000110050040000000111234567811",
        "establecimiento": "005",
        "puntoEmision": "004",
        "secuencial": "000000011",
        "ambiente": "PRODUCCION"
      },
      "store": {
        "code": "K004", "name": "Sucursal Amazonas",
        "address": "AV. AMAZONAS N36-15", "city": "Quito", "phone": "022222222",
        "govIdType": "RUC", "govIdNumber": "1791415132001",
        "secondaryGovIdType": null, "secondaryGovIdNumber": null
      },
      "company": {
        "legalName": "INT FOOD SERVICES CORP SA",
        "tradeName": "KFC",
        "govIdType": "RUC",
        "govIdNumber": "1791415132001",
        "headquartersAddress": "PICHINCHA / QUITO / INAQUITO / COREA 126 Y AV. AMAZONAS",
        "countryLines": [
          { "key": "granContribuyente", "label": "GRAN CONTRIBUYENTE", "value": "NAC-GCFOIOC21-00000900-E" },
          { "key": "contribuyenteEspecial", "label": "CONTRIBUYENTE ESPECIAL", "value": "155" },
          { "key": "obligadoContabilidad", "label": "Obligado a llevar contabilidad", "value": "SI" }
        ],
        "legends": [
          { "key": "avisoCambios", "text": "Estimado cliente: Por favor verifique los datos de su factura…" }
        ]
      },
      "graphic": { "qr": "1208202601000000000000110050040000000111234567811" },
      "policy": {
        "numberingFailure": {
          "action": "CONTINUE",
          "lostSaleReason": null,
          "resolvedAt": "2026-08-21T21:59:56.203Z",
          "configVersion": null,
          "resolvedFrom": { "requestStatus": "GENERATED" }
        }
      },
      "printing": { "printable": true, "mode": "FISCAL_DOCUMENT", "reason": null },
      "failure": null,
      "timestamps": {
        "requestedAt": "2026-08-12T23:57:46.7Z",
        "respondedAt": "2026-08-12T23:57:47.2Z",
        "providerLatencyMs": 406
      },
      "correlationId": null,
      "providerCode": "hio",
      "providerIdentity": { "name": "hio.fiscalization", "version": "dev", "reference": null },
      "providerMetadata": { "deviceUid": "4B8E21F9A0D35C7A", "externalStoreCode": "K000" }
    }
  }
  ```

  ```json 201 Numerado — Colombia (CO) theme={null}
  {
    "success": true,
    "data": {
      "fiscalRequestId": "c1a7f0e2-9b34-4d21-8e55-3f60ab12cd90",
      "orderCode": "CO-K039-1786901234",
      "idempotencyKey": "2f81dcb4-77a0-4c19-9e3b-5a04e7f1b2c8",
      "reused": false,
      "countryCode": "CO",
      "requestStatus": "GENERATED",
      "documentStatus": "PENDING",
      "retryable": false,
      "environment": "PRODUCTION",
      "document": {
        "documentType": "SALE_INVOICE",
        "documentLabel": "FACTURA ELECTRONICA DE VENTA",
        "documentNumber": "SETP990000001",
        "authorizationMode": "ONLINE",
        "authorizationLabel": "VALIDACION PREVIA",
        "issuedAt": "2026-08-16T14:21:03.118Z",
        "compensates": null
      },
      "countryData": {
        "numeroComprobante": "SETP990000001",
        "cufe": "9c4f1e… (96 caracteres hexadecimales)",
        "prefijo": "SETP",
        "numeroDian": "990000001",
        "qrCode": "https://catalogo-vpfe.dian.gov.co/document/searchqr?documentkey=9c4f1e…",
        "ambiente": "PRODUCCION"
      },
      "store": {
        "code": "K039", "name": "Sucursal Chapinero",
        "address": "CRA 13 # 63-39", "city": "Bogotá", "phone": "6013334444",
        "govIdType": "NIT", "govIdNumber": "9001234567",
        "secondaryGovIdType": null, "secondaryGovIdNumber": null
      },
      "company": {
        "legalName": "COMERCIALIZADORA ANDINA S.A.S.",
        "tradeName": "KFC",
        "govIdType": "NIT",
        "govIdNumber": "9001234567",
        "headquartersAddress": "BOGOTA D.C. / CHAPINERO / CRA 13 # 63-39",
        "countryLines": [
          { "key": "regimen", "label": "REGIMEN", "value": "RESPONSABLE DE IVA" },
          { "key": "resolucion", "label": "RESOLUCION DIAN", "value": "18760000001" }
        ],
        "legends": []
      },
      "graphic": null,
      "policy": {
        "numberingFailure": {
          "action": "CONTINUE",
          "lostSaleReason": null,
          "resolvedAt": "2026-08-21T21:59:56.203Z",
          "configVersion": null,
          "resolvedFrom": { "requestStatus": "GENERATED" }
        }
      },
      "printing": { "printable": true, "mode": "FISCAL_DOCUMENT", "reason": null },
      "failure": null,
      "timestamps": {
        "requestedAt": "2026-08-16T14:21:02.6Z",
        "respondedAt": "2026-08-16T14:21:03.3Z",
        "providerLatencyMs": 712
      },
      "correlationId": null,
      "providerCode": "hio",
      "providerIdentity": { "name": "hio.fiscalization", "version": "dev", "reference": null },
      "providerMetadata": { "externalStoreCode": "K039" }
    }
  }
  ```

  ```json 201 Anulado — nota de crédito theme={null}
  {
    "success": true,
    "data": {
      "fiscalRequestId": "b73b2fd9-c7a6-42bd-815f-d39c210983ef",
      "orderCode": "EC-K004-42-1786579046934",
      "idempotencyKey": "1e07b0e1-784b-40b9-8903-0326361ebefd",
      "reused": false,
      "countryCode": "EC",
      "requestStatus": "GENERATED",
      "documentStatus": "PENDING",
      "retryable": false,
      "environment": "PRODUCTION",
      "document": {
        "documentType": "CREDIT_NOTE",
        "documentLabel": "NOTA DE CREDITO RIDE",
        "documentNumber": "005-004-000000002",
        "authorizationMode": "ONLINE",
        "authorizationLabel": "EMISION NORMAL",
        "issuedAt": "2026-08-14T18:32:32.242Z",
        "compensates": {
          "documentNumber": "005-004-000000068",
          "issuedAt": "2026-08-14T18:31:57.649Z",
          "reason": "ORDER_CANCELLATION",
          "reasonLabel": "Anulación de pedido"
        }
      },
      "countryData": {
        "numeroComprobante": "005-004-000000002",
        "claveAcceso": "1408202604179141513200110050040000000021234567815",
        "establecimiento": "005",
        "puntoEmision": "004",
        "secuencial": "000000002",
        "ambiente": "PRODUCCION"
      },
      "policy": {
        "numberingFailure": {
          "action": "CONTINUE",
          "lostSaleReason": null,
          "resolvedAt": "2026-08-21T21:59:56.203Z",
          "configVersion": null,
          "resolvedFrom": { "requestStatus": "GENERATED" }
        }
      },
      "printing": { "printable": true, "mode": "FISCAL_DOCUMENT", "reason": null },
      "graphic": { "qr": "1408202604179141513200110050040000000021234567815" },
      "failure": null,
      "providerCode": "hio",
      "providerIdentity": { "name": "hio.fiscalization", "version": "dev", "reference": null },
      "providerMetadata": { "deviceUid": "4B8E21F9A0D35C7A", "externalStoreCode": "K000" }
    }
  }
  ```

  ```json 202 Sin respuesta del proveedor theme={null}
  {
    "success": true,
    "data": {
      "fiscalRequestId": "81670b02-4073-4e50-adca-e3c9917e7006",
      "orderCode": "EC-K004-42-1786579046934",
      "reused": false,
      "countryCode": "EC",
      "requestStatus": "PENDING",
      "documentStatus": "PENDING",
      "retryable": true,
      "environment": "PRODUCTION",
      "document": null,
      "countryData": null,
      "graphic": null,
      "policy": {
        "numberingFailure": {
          "action": "REFUND",
          "lostSaleReason": "FISCAL_NUMBERING_FAILED",
          "resolvedAt": "2026-08-21T21:59:56.203Z",
          "configVersion": "fnv1a:d096701f",
          "resolvedFrom": {
            "retryable": "true",
            "operation": "INVOICE",
            "requestStatus": "PENDING",
            "failureScope": "TECHNICAL",
            "failureCode": "PROVIDER_UNREACHABLE",
            "storeCode": "K004"
          }
        }
      },
      "printing": {
        "printable": false,
        "mode": "PROVISIONAL_RECEIPT",
        "reason": "AWAITING_FISCAL_NUMBERING"
      },
      "failure": {
        "code": "PROVIDER_UNREACHABLE",
        "scope": "TECHNICAL",
        "message": "No se pudo establecer comunicación con el proveedor fiscal."
      },
      "providerCode": "hio",
      "providerIdentity": { "name": null, "version": null, "reference": null },
      "providerMetadata": null
    }
  }
  ```

  ```json 202 Proveedor no disponible theme={null}
  {
    "success": true,
    "data": {
      "fiscalRequestId": "c4f0a1e2-5d3b-4f77-9a10-6b2e8c4d1f03",
      "orderCode": "EC-K000-42-1786579046934",
      "reused": false,
      "countryCode": "EC",
      "requestStatus": "FAILED_RETRYABLE",
      "documentStatus": "PENDING",
      "retryable": true,
      "environment": "PRODUCTION",
      "document": null,
      "countryData": null,
      "graphic": null,
      "policy": {
        "numberingFailure": {
          "action": "CONTINUE",
          "lostSaleReason": "FISCAL_NUMBERING_FAILED",
          "resolvedAt": "2026-08-21T21:59:56.203Z",
          "configVersion": null,
          "resolvedFrom": {
            "retryable": "true",
            "operation": "INVOICE",
            "requestStatus": "FAILED_RETRYABLE",
            "failureScope": "TECHNICAL",
            "failureCode": "PROVIDER_UNAVAILABLE",
            "storeCode": "K000"
          }
        }
      },
      "printing": {
        "printable": false,
        "mode": "PROVISIONAL_RECEIPT",
        "reason": "AWAITING_FISCAL_NUMBERING"
      },
      "failure": {
        "code": "PROVIDER_UNAVAILABLE",
        "scope": "TECHNICAL",
        "message": "El servicio de fiscalización no está disponible."
      },
      "providerCode": "hio",
      "providerIdentity": { "name": "hio.fiscalization", "version": "dev", "reference": null },
      "providerMetadata": null
    }
  }
  ```

  ```json 200 Rechazado theme={null}
  {
    "success": true,
    "data": {
      "fiscalRequestId": "1d10c5b9-9f1b-4c62-a425-238110ce9cd6",
      "orderCode": "EC-UIO-9-1786580272803",
      "reused": false,
      "countryCode": "EC",
      "requestStatus": "FAILED_FINAL",
      "documentStatus": "PENDING",
      "retryable": false,
      "environment": "PRODUCTION",
      "document": null,
      "countryData": null,
      "graphic": null,
      "policy": {
        "numberingFailure": {
          "action": "REFUND",
          "lostSaleReason": "FISCAL_NUMBERING_FAILED",
          "resolvedAt": "2026-08-21T21:59:56.203Z",
          "configVersion": "fnv1a:d096701f",
          "resolvedFrom": {
            "retryable": "false",
            "operation": "INVOICE",
            "requestStatus": "FAILED_FINAL",
            "failureScope": "FUNCTIONAL",
            "failureCode": "FISCAL_BUSINESS_RULE",
            "storeCode": "UIO"
          }
        }
      },
      "printing": {
        "printable": false,
        "mode": "PROVISIONAL_RECEIPT",
        "reason": "FISCAL_REJECTED"
      },
      "failure": {
        "code": "FISCAL_BUSINESS_RULE",
        "scope": "FUNCTIONAL",
        "message": "identidad fiscal de tienda no configurada: EC / tienda EC-UIO-001"
      },
      "providerCode": "hio",
      "providerIdentity": { "name": "hio.fiscalization", "version": "dev", "reference": null },
      "providerMetadata": null
    }
  }
  ```

  ```json 200 Tienda sin numeración theme={null}
  {
    "success": true,
    "data": {
      "fiscalRequestId": null,
      "orderCode": "EC-K000-42-1786579046934",
      "idempotencyKey": null,
      "reused": false,
      "countryCode": "EC",
      "requestStatus": "NOT_APPLICABLE",
      "documentStatus": null,
      "retryable": false,
      "environment": null,
      "document": null,
      "countryData": null,
      "graphic": null,
      "policy": {
        "numberingFailure": {
          "action": "CONTINUE",
          "lostSaleReason": null,
          "resolvedAt": "2026-08-21T21:59:56.203Z",
          "configVersion": null,
          "resolvedFrom": { "requestStatus": "NOT_APPLICABLE" }
        }
      },
      "printing": { "printable": false, "mode": "NONE", "reason": "NUMBERING_DISABLED" },
      "failure": null,
      "store": {
        "code": "K000",
        "name": "Laboratorio Ecuador",
        "govIdType": "RUC",
        "govIdNumber": "1791415132001"
      },
      "company": {
        "legalName": "INT FOOD SERVICES CORP S.A.",
        "tradeName": "KFC",
        "countryLines": [],
        "legends": []
      },
      "timestamps": {
        "requestedAt": "2026-08-14T01:01:14.7Z",
        "respondedAt": "2026-08-14T01:01:14.7Z",
        "providerLatencyMs": null
      },
      "correlationId": null,
      "providerCode": null,
      "providerIdentity": null,
      "providerMetadata": null
    }
  }
  ```

  ```json 409 Conflicto theme={null}
  {
    "success": false,
    "error": "CONFLICT",
    "message": "La Idempotency-Key ya fue usada con un cuerpo distinto"
  }
  ```
</ResponseExample>
