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

# Resumen de eventos

> Fire es event-driven. Cuando algo significativo pasa dentro de Fire, un evento se entrega a tu endpoint vía Integration Flows que tú configuras en el dashboard.

Fire emite **eventos** cada vez que ocurre una transición de negocio relevante — una orden se completa, una orden se cancela, un documento fiscal es autorizado por la autoridad tributaria, la cocina avanza un pedido. Cada evento se entrega a tu endpoint por un **Integration Flow** que configuras en el dashboard de Fire.

<Note>
  Fire emite **cinco** tipos de evento de orden hoy: [`order.completed`](/es/events/order-completed), [`order.cancelled`](/es/events/order-cancelled), [`order.invoiced`](/es/events/order-invoiced), [`order.reversed`](/es/events/order-reversed) y [`order.status_updated`](/es/events/order-status-updated), más el evento programado [`store.business_day_closed`](/es/events/store-day-closed) al cierre del día.
</Note>

## Fire es la fuente de verdad del ciclo de vida

Una orden puede nacer en cualquier canal — POS, kiosk, app, agregador — pero desde que entra, **Fire la normaliza y toma control de su ciclo de vida completo**. Todo lo que pasa alrededor de esa orden se centraliza en Fire: el pago la completa, la cocina la avanza, el proveedor fiscal la factura o la reversa, y el cierre del día la consolida. Cada uno de esos hitos sale hacia tus sistemas como un evento con el mismo snapshot canónico de la orden.

Esa centralización funciona en ambas direcciones: los sistemas externos **no escriben estado por su cuenta** — reportan a Fire por webhooks entrantes, y **cada reporte debe referenciar un evento que Fire emitió** (el `eventId` del envelope que recibiste). Fire valida esa referencia antes de aceptar el reporte; un callback que no apunte a un evento emitido por Fire para esa orden se rechaza con `400`. Así, la línea de tiempo de una orden tiene una sola fuente de verdad y nunca se bifurca.

<Note>
  El **diagrama de secuencia** muestra el ciclo de vida en el tiempo — incluyendo los dos *round-trips* donde el proveedor fiscal y el KDS **resuelven y reportan de vuelta a Fire**. El **diagrama estructural** lo resume en quién habla con quién.
</Note>

### Ciclo de vida en el tiempo

```mermaid theme={null}
sequenceDiagram
    autonumber
    participant CH as 🛒 Canal
    participant F as 🔥 FIRE
    participant FP as 🧾 Proveedor fiscal
    participant AU as 🏛️ Autoridad fiscal
    participant K as 👨‍🍳 KDS · cocina
    participant Y as 🔌 Tu sistema

    CH->>F: orden (cualquier canal)
    Note over F: Fire normaliza y toma el control del lifecycle
    F->>Y: order.completed
    F->>Y: order.cancelled (si se cancela)

    rect rgba(99, 102, 241, 0.14)
    Note over F,AU: Ciclo fiscal — el proveedor resuelve y reporta
    F->>FP: solicita emisión
    FP->>AU: emite el documento
    AU-->>FP: autoriza / cancela
    FP->>F: callback fiscal (referencia un evento de Fire)
    F-->>FP: 202
    F->>Y: order.invoiced / order.reversed
    end

    rect rgba(16, 185, 129, 0.14)
    Note over F,K: Ciclo de cocina — el KDS reporta
    F->>K: despacha la orden
    K->>F: reporta avance (preparing · ready · dispatched)
    F->>Y: order.status_updated
    end

    Note over F: cierre del día (programado)
    F->>Y: store.business_day_closed
```

### Quién habla con quién

```mermaid theme={null}
%%{init: {'flowchart': {'nodeSpacing': 120, 'rankSpacing': 190, 'padding': 18, 'useMaxWidth': true}, 'themeVariables': {'fontSize': '15px'}}}%%
flowchart LR
    CH(["🛒 POS · kiosk · app · agregador"])
    FIRE(("FIRE"))
    FACT["🧾 Facturador / proveedor fiscal"]
    AUT["🏛️ Autoridad fiscal<br/>SEFAZ · SRI · DIAN…"]
    KDS["👨‍🍳 KDS — cocina"]
    DOWN["🔌 Tus integraciones<br/>ERP · app · agregador · archivo"]

    CH -- "① orden" --> FIRE
    FIRE -- "② order.completed / order.cancelled" --> FACT
    FACT <-- "③ emite / anula" --> AUT
    FACT -- "④ callback fiscal<br/>(autorizada · anulada)" --> FIRE
    FIRE -- "⑤ order.invoiced / order.reversed" --> DOWN
    FIRE -- "⑥ orden a cocina" --> KDS
    KDS -- "⑦ reporta avance<br/>(preparing · ready · dispatched)" --> FIRE
    FIRE -- "⑧ order.status_updated" --> DOWN
    FIRE -- "⑨ store.business_day_closed (cierre del día)" --> DOWN

    style FIRE fill:#1E293B,color:#F8FAFC,stroke:#1E293B
```

Fíjate en los **dos circuitos de ida y vuelta**: Fire le avisa al facturador (②), el facturador resuelve con la autoridad (③) y **le responde a Fire** por el callback (④) — recién entonces Fire emite `order.invoiced` / `order.reversed` (⑤). Lo mismo con cocina: Fire despacha la orden al KDS (⑥), el KDS **reporta el avance a Fire** (⑦) y Fire emite `order.status_updated` (⑧). Nada se entera "por su cuenta": todo pasa por Fire y sale de Fire — incluido el `store.business_day_closed` (⑨) al cierre del día.

### Una acción, un evento

Cada acción de negocio es su **propio** evento con su **propio** `event.id` — `order.invoiced` para una autorización fiscal, `order.reversed` para una cancelación, cada `order.status_updated` para una transición de cocina. Nunca se colapsan: autorizar una orden y luego cancelarla son dos eventos distintos con dos ids distintos.

Esto define cómo Fire matchea los **reportes inbound** que disparan esas acciones:

* **Un reporte referencia el evento de *esa* acción.** Cuando le reportás una cancelación a Fire, ecoa el `event.id` del evento de cancelación — no el de la autorización. Cada `(orderId, eventId)` se ingresa exactamente una vez.
* **Reenviar el mismo reporte es seguro.** El mismo `(orderId, eventId)` con el mismo tipo es un replay idempotente — Fire devuelve el registro existente y no procesa nada dos veces (`202`, `duplicate: true`). Podés regenerar tu propio id de proveedor libremente.
* **No podés colgar una acción del evento de otra.** Reportar una cancelación reusando el `eventId` de la autorización se rechaza con **`409`** — aceptarlo te diría que la cancelación tuvo éxito mientras Fire nunca canceló. Cada acción debe referenciar su propio evento emitido por Fire.

<Note>
  **Una excepción — el recorrido de cocina.** El KDS reporta `preparing` → `ready` → `dispatched` contra **un** `eventId` (el del dispatch), distinguidos por `eventType` — así que el mismo `eventId` con un type distinto es un **paso nuevo**, no un conflicto. La regla de arriba (un `eventId` por acción) aplica para fiscal (una acción = un evento); el recorrido del KDS comparte un evento de dispatch entre sus pasos. En ambos casos, un reporte debe referenciar un evento emitido por Fire, y la misma `(orderId, eventId, eventType)` se ingresa una vez.
</Note>

Ver el [callback fiscal](/es/api-reference/fiscal-callback#idempotencia-y-escenarios) y el [endpoint KDS](/es/api-reference/kds-order-status#idempotencia-y-el-recorrido) para las matrices completas de comportamiento inbound.

## Cómo funciona

```mermaid theme={null}
flowchart LR
    A([Algo pasa<br/>en Fire]) --> B([Fire envía un<br/>webhook]) --> C([Tu endpoint<br/>procesa y responde 200])
```

Cuando ocurre un evento de negocio relevante, Fire lo despacha a través de un flow que configuraste en el dashboard. El flow renderiza un body de petición HTTP y la hace POST a tu endpoint. Tú confirmas con una respuesta `2xx`.

La mecánica interna (queues, reintentos, resolución de templates) está en [Entrega y reintentos](/es/events/delivery). El modelo de configuración de flows está en [Integration Flows](/es/events/integration-flows).

## Qué recibe tu endpoint

Cuando dispara un flow, tu endpoint recibe un `POST` HTTP. El body tiene **tres campos top-level** — `event`, `data` y `_meta`:

```json theme={null}
{
  "event": {
    "id": "0d6e8a1c-1e7a-4b4f-8a3a-74ab0e9a9b21",
    "type": "order.completed",
    "createdAt": "2026-05-06T13:42:11.812Z"
  },
  "data": {
    /* snapshot V4 específico del evento — ver cada referencia */
  },
  "_meta": {
    "executionId":     "0d6e8a1c-1e7a-4b4f-8a3a-74ab0e9a9b21",
    "flowId":          "9a2b3c4d-7e8f-4a1b-9c2d-3e4f5a6b7c8d",
    "flowName":        "Integración ERP",
    "attempt":         "1",
    "triggerEntityId": "f1e2d3c4-b5a6-4789-9012-3456789abcde"
  }
}
```

### `event`

<ResponseField name="event" type="object">
  Identifica esta entrega.

  <Expandable title="event">
    <ResponseField name="id" type="string">
      Identificador único de esta entrega (el ID de ejecución del flow). **Úsalo como llave de idempotencia** — Fire puede entregar el mismo evento más de una vez en reintentos.
    </ResponseField>

    <ResponseField name="type" type="string">
      Tipo de evento. Uno de: `order.completed`, `order.cancelled`, `order.invoiced`, `order.reversed`, `order.status_updated`, `store.business_day_closed`.
    </ResponseField>

    <ResponseField name="createdAt" type="string">
      Timestamp ISO 8601 UTC del momento en que el evento se materializó dentro de Fire (no el momento en que llegó a tu endpoint).
    </ResponseField>
  </Expandable>
</ResponseField>

### `data`

<ResponseField name="data" type="object">
  Payload específico del evento. La forma varía por evento:
</ResponseField>

| Evento                                                    | Qué hay en `data`                                                                                                      |
| --------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------- |
| [`order.completed`](/es/events/order-completed)           | Snapshot V4 de la orden — order, store (con `storeFiscalConfig` opcional), customer, payments, fulfillment, KDS, lines |
| [`order.cancelled`](/es/events/order-cancelled)           | Snapshot V4 + bloque `cancellation` de auditoría                                                                       |
| [`order.invoiced`](/es/events/order-invoiced)             | Snapshot V4 con `order.fiscal` (chave, protocolo, número…) + `storeFiscalConfig`                                       |
| [`order.reversed`](/es/events/order-reversed)             | Snapshot V4 + `xmlCancelamento` + `sefazCancellation`                                                                  |
| [`order.status_updated`](/es/events/order-status-updated) | Snapshot V4 + bloque `kitchen` (status, previousStatus, history del recorrido)                                         |

### `_meta`

<ResponseField name="_meta" type="object">
  Trazabilidad a nivel de ejecución. Loguéalo junto al evento para debuggear problemas de entrega y correlacionar reintentos.

  <Expandable title="_meta">
    <ResponseField name="executionId" type="string">
      ID interno de ejecución del flow. Espeja `event.id`.
    </ResponseField>

    <ResponseField name="flowId" type="string">
      UUID del Integration Flow que produjo esta entrega.
    </ResponseField>

    <ResponseField name="flowName" type="string">
      Nombre legible del flow (tal como está en el dashboard).
    </ResponseField>

    <ResponseField name="attempt" type="string">
      Número de intento de entrega, empezando en `"1"`. Incrementa con cada reintento.
    </ResponseField>

    <ResponseField name="triggerEntityId" type="string">
      ID de la entidad de negocio que originó el evento (típicamente el UUID de la orden, mismo que `data.orderId`).
    </ResponseField>
  </Expandable>
</ResponseField>

<Note>
  Las claves `event`, `data` y `_meta` vienen del **template canónico de body** que trae el dashboard de Fire. Son una convención, no un envelope Fire fijo — si el nodo HTTP de tu flow define otro template, la forma del wire cambia. Ver [Customizando el body](#customizando-el-body) más abajo.
</Note>

## Entrega HTTP por defecto

| Campo          | Default                                                                           |
| -------------- | --------------------------------------------------------------------------------- |
| Método         | `POST` (configurable por flow)                                                    |
| `Content-Type` | `application/json` (lo setean los headers del nodo HTTP de tu flow)               |
| Timeout        | 30 segundos (configurable, 1–60s)                                                 |
| Auth           | None / `Bearer` / `x-api-key` / OAuth2 client credentials — se elige por flow     |
| Firma          | HMAC-SHA256 **opcional** (nodo Webhook) — `X-Fire-Signature` + `X-Fire-Timestamp` |
| Reintentos     | Hasta 5 con backoff exponencial                                                   |
| Dead letter    | Después de los reintentos, la ejecución cae en `flow_dead_letter`                 |

<Note>
  Las peticiones salientes pueden ir **firmadas con HMAC** si tu flow usa el **nodo Webhook**: Fire agrega `X-Fire-Signature` (`v1=hex(HMAC-SHA256(secret, "{timestamp}.{body}"))`) y `X-Fire-Timestamp` (Unix seconds). La firma es opcional y **complementa** al auth de transporte (Bearer / API key / OAuth2). Detalle y verificación en [el nodo Webhook firmado](/es/events/integration-flows#nodo-webhook-firmado-hmac).
</Note>

## Recibiendo eventos

```js theme={null}
import express from "express";

const app = express();
const seen = new Map(); // reemplaza por Redis / DB en producción

app.post("/fire/events", express.json(), async (req, res) => {
  const { event, data, _meta } = req.body ?? {};
  if (!event?.id || !event?.type) return res.status(400).end();

  // 1. Confirma primero para liberar la conexión de Fire
  res.status(200).end();

  // 2. Deduplica por event.id (el ID de ejecución del flow)
  if (seen.has(event.id)) return;
  seen.set(event.id, Date.now());

  // 3. Loggea _meta para trazabilidad — invaluable al debuggear
  console.log("Evento Fire", { eventId: event.id, type: event.type, _meta });

  // 4. Despacha por type
  switch (event.type) {
    case "order.completed":     return onOrderCompleted(data);
    case "order.cancelled":     return onOrderCancelled(data);
    case "order.invoiced":       return onOrderInvoiced(data);
    case "order.reversed":       return onOrderReversed(data);
    case "order.status_updated": return onKitchenAdvance(data);
    default: console.warn("Tipo de evento Fire desconocido", event.type);
  }
});

app.listen(8080);
```

<Steps>
  <Step title="Confirma rápido">
    Devuelve `200 OK` (o cualquier `2xx`) en menos de 30 segundos — idealmente bajo 1 segundo. Confirma **antes** de hacer trabajo pesado.
  </Step>

  <Step title="Autentica la petición">
    Valida la credencial de auth que envía el flow (Bearer token, API key u OAuth2 access token). Rechaza cualquier petición que no haga match.
  </Step>

  <Step title="Deduplica">
    Busca `event.id` en un store de TTL corto (Redis con expiración de 24 horas funciona). Si ya lo procesaste, salta.
  </Step>

  <Step title="Valida el payload">
    Chequea que `event.type` sea lo que esperas y que el bloque `data` tenga los campos esperados. Devuelve `400` ante inputs malformados para que no entren en tu cola de retry desde Fire.
  </Step>

  <Step title="Procesa y persiste">
    Aplica tu lógica de negocio, persiste el resultado por `event.id` para trazabilidad. Devuelve `5xx` para errores transitorios (Fire reintenta); devuelve `4xx` para input malo no recuperable (Fire lo manda a dead letter).
  </Step>
</Steps>

## Customizando el body

El body que Fire entrega a tu endpoint es **lo que renderice el template del body del nodo HTTP de tu flow**. El template canónico (arriba) es el default recomendado, pero puedes escribir cualquier template JSON válido que referencie el **trigger context** del runtime:

```ts theme={null}
{
  trigger: {
    event: { id, type, createdAt },
    data: V4Snapshot,         // payload específico del evento
  },
  execution: { id, ... },     // metadata de ejecución
  flow:      { id, name },    // identidad de tu flow
  queue:     { id, attempt, triggerEntityId },
}
```

El template canónico usa estos paths:

```json theme={null}
{
  "event": {
    "id":        "{{trigger.event.id}}",
    "type":      "{{trigger.event.type}}",
    "createdAt": "{{trigger.event.createdAt}}"
  },
  "data": "{{trigger.data}}",
  "_meta": {
    "executionId":     "{{execution.id}}",
    "flowId":          "{{flow.id}}",
    "flowName":        "{{flow.name}}",
    "attempt":         "{{queue.attempt}}",
    "triggerEntityId": "{{queue.triggerEntityId}}"
  }
}
```

Puedes escribir otro template que omita `_meta`, aplane `data`, renombre claves, envíe solo campos específicos, etc. Consulta [Integration Flows](/es/events/integration-flows) para la referencia completa.

## Próximo

<CardGroup cols={2}>
  <Card title="Integration Flows" icon="diagram-project" href="/es/events/integration-flows">
    Cómo tu sistema se suscribe a eventos a través de flows configurados en el dashboard.
  </Card>

  <Card title="Entrega y reintentos" icon="repeat" href="/es/events/delivery">
    Política de retry, comportamiento de dead-letter, idempotencia y garantías de orden.
  </Card>

  <Card title="order.completed" icon="receipt" href="/es/events/order-completed">
    El evento más común — snapshot V4 completo de la orden.
  </Card>

  <Card title="order.invoiced" icon="file-invoice" href="/es/events/order-invoiced">
    Autorización fiscal brasileña vía tu proveedor fiscal.
  </Card>
</CardGroup>
