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

# Integrating fiscalization

> How the work is split between your channel, Fire, the fiscal provider and the tax authority — and what your point of sale must do at each step.

Fire does not issue receipts: it **numbers** them. That distinction explains almost every
design decision, so it is worth starting there.

## Who does what

<CardGroup cols={2}>
  <Card title="Your channel" icon="cash-register">
    Charges, requests the numbering, prints and injects the order. It knows no country's
    fiscal rules.
  </Card>

  <Card title="Fire" icon="server">
    Resolves the store, the issuer and the provider. Stores the request, asks for the
    numbers, and tells you what can be printed.
  </Card>

  <Card title="The fiscal provider" icon="stamp">
    Translates to the authority's codes, numbers with its country vocabulary, and submits
    the document.
  </Card>

  <Card title="The tax authority" icon="landmark">
    Approves or rejects. Its verdict arrives **after** the customer has left with the
    receipt.
  </Card>
</CardGroup>

<Info>
  **One contract, every country.** Fire resolves internally which provider matches each
  country. Your integration is the same in Ecuador, Brazil or Venezuela: what changes is
  which fields come filled in the response, not how you ask for it.
</Info>

## The timeline

```mermaid theme={null}
sequenceDiagram
    participant POS as Your channel
    participant Fire
    participant Prov as Fiscal provider
    participant Auth as Tax authority
    participant Cons as Your consumers

    POS->>Fire: 1. POST /fiscal/numbering
    Fire->>Prov: 2. requests the numbering
    Prov-->>Fire: document + countryData (the country vocabulary)
    Fire-->>POS: 3. document + graphic + printing
    POS->>POS: 4. prints the receipt
    POS->>Fire: 5. injects the order (same orderCode)
    Fire->>Fire: 6. compensates the order with its numbering
    Fire-->>Cons: order.opened / order.completed / order.cancelled
    Prov->>Auth: 7. submits the document
    Auth-->>Prov: approved
    Prov-->>Fire: 8. result
    Fire-->>Cons: order.invoiced / order.reversed
```

Steps 1 to 5 happen **with the customer waiting at the register**: seconds. From 6 onwards
your channel is no longer involved.

## Where the fiscal data shows up

The numbering does not stay locked inside this endpoint. It travels through the order
lifecycle at **two distinct moments**, and they should not be conflated.

### 1. What we compensate at injection

When you inject the order, Fire enriches it with the numbering you already obtained. That
data travels in the normal lifecycle events:

| Event                                           | When                                                              |
| ----------------------------------------------- | ----------------------------------------------------------------- |
| [`order.opened`](/en/events/order-opened)       | The order was opened — carries its numbering if already requested |
| [`order.completed`](/en/events/order-completed) | Payment settled the full total                                    |
| [`order.cancelled`](/en/events/order-cancelled) | The order was cancelled before or after payment                   |

At this point **there is still no verdict from the authority**: there are printed numbers and
a registered sale.

### 2. What the fiscal callback compensates

Minutes later, the provider tells Fire what the authority decided. That callback is what
triggers the two outcome events:

| Event                                         | When                                                                           |
| --------------------------------------------- | ------------------------------------------------------------------------------ |
| [`order.invoiced`](/en/events/order-invoiced) | The authority **approved** the document — arrives with key and protocol        |
| [`order.reversed`](/en/events/order-reversed) | The authority **confirmed the cancellation** of an already authorized document |

<Info>
  That is the separation to keep clear end to end: **you request the numbering and it
  compensates the order; the authorization arrives on its own and compensates the outcome.**
  A numbered document may never reach `order.invoiced` if the authority rejects it.
</Info>

If you already consume order events, **you do not have to query anything**: the fiscal data
arrives through the same path as the rest of the sale. Direct querying is for support and for
when you lose the synchronous response.

### The field to read: `data.fiscalRepresentation`

The numbering arrives in that block, on **every** order event — all five in the two tables
above.

**The key always travels.** It arrives as `null` when numbering was not attempted —
aggregators, countries without fiscal representation, or merchants with numbering turned off
— and carries the block when it was. Branch on the value, never on the key's presence:

```js theme={null}
if (data.fiscalRepresentation) {
  // numbering was attempted — numberingStatus tells you how it went
  print(data.fiscalRepresentation.documentNumber)
}
```

Carrying the block means **numbering was attempted, not that it succeeded**. A charged sale
left **with no fiscal receipt** arrives with `null` identifiers and the reason in `failure` —
handle that case, because it used to be invisible.

And carrying the block does **not** mean the document is authorized either: that is what
`lastKnown.fiscal.status` says, and it is the only one of the two that gets updated.

#### What the block carries

Three layers, and it pays not to mix them:

| Layer         | What is in it                                                                                       | How to read it                                                                                                                         |
| ------------- | --------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------- |
| **Canonical** | `documentType`, `documentNumber`, `issuedAt`, `authorizationMode`, `numberingStatus`, `environment` | They mean the same in every country. This is what you program against                                                                  |
| **Country**   | `countryData`                                                                                       | The vocabulary of the authority that numbered: `claveAcceso` in Ecuador, `numeroControl` in Venezuela. **Iterate it, do not index it** |
| **Provider**  | `providerCode`, `providerIdentity`, `providerMetadata`                                              | Diagnostics. See below                                                                                                                 |

<Warning>
  **The number that gets printed is `documentNumber`. Do not compose it yourself.**

  It arrives already composed by the provider, who knows their country's rule — in Ecuador,
  article 18 of the Reglamento de Comprobantes de Venta: fifteen digits in three segments. The
  country block carries the same value under the name the authority uses (`numeroComprobante`
  in Ecuador).

  Assembling it by hand from `establecimiento`, `puntoEmision` and `secuencial` looks
  equivalent and is not: the Reglamento allows omitting the sequential's leading zeros, so
  `001-020-123` can be just as legal as `001-020-000000123`. Compose it yourself and you print
  a number in **your** convention, not the one the document was issued with.
</Warning>

The three provider fields are **not the same thing**, which is why they travel apart:

* **`providerCode`** is **our** adapter identifier (`hio`). It says which integration numbered.
* **`providerIdentity`** belongs to the provider and **has a shape**: `name`, `version` and
  `reference`. That `reference` is the one you quote back to them when a case needs escalating
  — it is not your `Idempotency-Key`.
* **`providerMetadata`** is an **opaque bag**: no guaranteed shape, the provider owns the keys
  and may change them without notice. It is for pasting into a support ticket.

<Warning>
  **Do not branch on `providerMetadata` keys.** Programming against them ties your integration
  to whichever provider numbers today, and they change without versioning the contract. If a
  piece of data matters enough to decide with, it will be in the canonical layer or in
  `countryData`.
</Warning>

The block also carries **the order's current document**, not the first one: if a cancellation
happened, the credit note is on top, `compensates` points at the invoice it voids, and the
whole invoice sits in `history`. Every `history` entry has **exactly the same keys** as the
block above, so they read the same.

The full field-by-field reference lives in
[`order.opened`](/en/events/order-opened#data-fiscalrepresentation).

## The three rules to understand

<AccordionGroup>
  <Accordion title="Numbered is not authorized" icon="scale-balanced">
    The response carries **two states, and they are never merged**:

    * `requestStatus` — did I get numbers to print?
    * `documentStatus` — did the authority approve it?

    At numbering time the second one is **always** `PENDING`. That is not a problem: in most
    countries the receipt is handed over before the authority sees it. If your integration
    collapses both into one field, at some point you will tell a customer their invoice is
    authorized when it only has a number.
  </Accordion>

  <Accordion title="Fire never stops you, but the account can" icon="shield-check">
    Fire **never** returns an error that leaves you guessing. Whatever fails, the response
    carries an explicit decision in `policy.numberingFailure.action`.

    What changed: that decision **is not always to carry on**. The account configures, per
    vendor, what happens when numbering did not work.

    * **`CONTINUE`** — the default, and the behaviour you already know. Print according to
      `printing.mode` — usually a non-fiscal ticket — and inject the order anyway. The receipt
      is resolved later.
    * **`REFUND`** — give the money back at the counter and **do not inject the order**. That
      sale did not happen; it has to be [reported](/en/api-reference/lost-sales) or no
      trace remains that money changed hands.

    **In both cases: do not hold the sale or retry in a loop with the customer in front of
    you.** The money has already been charged.

    <Warning>
      **With `CONTINUE`, nothing completes it on its own.** To get the receipt you must call
      again with the **same `orderCode`** — Fire resumes the request and asks the provider
      again. A `202` nobody retries stays that way forever.

      With `REFUND` it is the opposite: **do not retry**. Numbering a sale you refunded would
      produce a receipt for something that did not happen.
    </Warning>
  </Accordion>

  <Accordion title="Fire decides what gets printed, not you" icon="print">
    The `printing` block is not a deduction from whether there is a document: it is a
    **legal rule of the country**. In Ecuador under contingency the receipt exists and is
    printed even though the authority has not seen it yet; in a country that forbids printing
    before approval, `printable` would come as `false` with the document present.

    If each channel derived that rule on its own, one of them would get it wrong — and that
    mistake only surfaces in an audit.
  </Accordion>
</AccordionGroup>

## Idempotency: three layers

A sale charged twice is a money problem; **a sale numbered twice is a fiscal problem**, and
it cannot be fixed with a deploy. Hence three barriers:

<Steps>
  <Step title="Your Idempotency-Key">
    You generate it, one per sale, and reuse it on every retry of that same sale. It is what
    keeps a network failure from consuming a second sequential.

    If you reuse it with a different body, Fire answers `409`: those are two different
    operations.
  </Step>

  <Step title="The natural key">
    `country + orderCode + operation`. It protects you even if your channel regenerates the
    `Idempotency-Key` on every attempt — the most common implementation mistake.

    It is also why the `orderCode` **must be unique per account and country**: if two stores
    use the same one, Fire stops before issuing.
  </Step>

  <Step title="The provider's">
    The same triple, on the other side. Both keys being identical is what makes a collision
    surface on both ends at once, instead of showing up months later as two receipts for one
    sale.
  </Step>
</Steps>

## Cancelling

Requested with `operation: "CANCEL"` and **the original sale's `orderCode`**. Nothing else.

Your point of sale **does not need to store any identifier of ours**: Fire finds the original
document through the natural key. That is deliberate — a kiosk that gets reinstalled or a
register that gets replaced would lose that data, and the sale could never be voided. The
`orderCode`, on the other hand, is printed on the ticket.

Which instrument materializes the cancellation is decided by the country: in Ecuador it is a
credit note with its own sequential; in Brazil, a cancellation event that produces no new
document.

**From then on the order carries the credit note on top.** The following events bring
`documentType: "CREDIT_NOTE"` in `fiscalRepresentation`, with `compensates` pointing at the
voided invoice and the whole invoice in `history`.

<Warning>
  **If your reconciliation assumes `documentNumber` is always the sale's, it breaks here.** The
  number on top becomes the credit note's. What the customer walked away with is not lost —it
  is in `history`— but you have to go there for it.
</Warning>

## If you lose the response

It happens: the network drops right after Fire numbered. The receipt exists and you do not
have it.

```
GET /api/v1/external/fiscal/numbering?orderCode=EC-K004-42-1786579046934
```

Returns `items[]` with every document of that order — possibly two, the invoice and its
cancellation. This is why the `orderCode` must be the same string in the numbering and in the
injection: it is the only thing left in your hand.

## Before going to production

<Check>Your `orderCode` is unique per account and country, and identical in numbering and injection.</Check>
<Check>You store the `Idempotency-Key` with the order and reuse it on retries.</Check>
<Check>Each device declares its own `device.externalId` — two registers in a store do not share one.</Check>
<Check>You branch on `printing.mode`, not on the HTTP status code.</Check>
<Check>You print `documentNumber` exactly as it arrives, without recomposing it from `establecimiento`, `puntoEmision` and `secuencial`.</Check>
<Check>You iterate `countryData` instead of indexing fixed keys: the numbering country sets them, and in Venezuela there is no `claveAcceso`.</Check>
<Check>You iterate over the keys of `graphic` instead of looking for fixed fields.</Check>
<Check>You do not branch on `providerMetadata` keys: it is the provider’s opaque bag and it changes without notice.</Check>
<Check>Your reconciliation accounts for the credit note being the top document after a cancellation, with the invoice in `history`.</Check>
<Check>On `202` you print provisional and inject anyway, without retrying in a loop.</Check>

<Card title="Endpoint reference" icon="code" href="/en/api-reference/fiscal-documents">
  Fields, responses and per-country examples.
</Card>
