Skip to main content
You are reading the current (v2.2) contract for order.cancelled. v2.2 adds data.fiscalRepresentation: the fiscal numbering the point of sale obtained before injecting the order. It always travels: null when numbering was not attempted, and populated when it was — numberingStatus tells you how it ended. Carrying content does not mean the document is authorized. Additive only — nothing you already read changed.The block carries the order’s current fiscal document: the fields that mean the same in every country at the top, the authority identifiers inside countryData in that country’s own vocabulary, and the previous documents in history. When a cancellation numbers, the credit note moves to the top and the invoice moves down into history — with compensates pointing at it.
order.cancelled fires when a previously injected order is cancelled — either from the Fire backoffice UI, an external adapter, or a cancellation API call. It does not retract a prior order.completed for the same order; both events are emitted independently.

Trigger condition

Fire emits order.cancelled once when an order’s status transitions to CANCELLED, regardless of the order’s previous payment state. The cancellation is recorded with full audit context (who, when, why, source).

What’s in trigger.data

Same V4 order snapshot as order.completed — every field documented there is present here, with three differences:
  1. status is "CANCELLED" (not "COMPLETED").
  2. paymentStatus is unchanged from when the order was completed (typically "SUCCEEDED" if the order had been paid before cancellation).
  3. A new top-level cancellation block carries the audit metadata.

Example — real production payload (BR, sanitized)

data.cancellation reference

object
Audit block describing how, when, and by whom the order was cancelled.

Lifecycle relative to other events

For a Brazilian fiscal-enabled order that gets cancelled, expect this sequence:
  • order.cancelled is emitted immediately when the cancellation happens, before any external fiscal authority is contacted.
  • order.reversed is emitted later — once SEFAZ confirms via your fiscal provider. It can arrive seconds or minutes after order.cancelled, depending on SEFAZ response time.
  • For non-Brazilian orders or stores without fiscal emission, only order.cancelled fires.

Country variations

order.cancelled is global — it fires for every country and every channel when an order is cancelled (Argentina, Brazil, Chile, Colombia, Ecuador, Venezuela, and any other country with active Integration Flows). The cancellation audit block (cancellation.{cancellationId, cancelledAt, cancelledBy, cancellationReason, cancellationSource}) is identical across countries. The only country-specific field is cancellation.metadata.fiscal, which is populated only for Brazilian stores that had a previously authorized fiscal document (i.e. a order.invoiced was emitted for this order earlier). For all other countries — and for BR orders cancelled before fiscal authorization — cancellation.metadata.fiscal is null and no order.reversed event will follow. For non-BR stores (Argentina, Chile, Colombia, Ecuador, Venezuela, others), the cancellation block looks like:
Non-BR cancellation
Store-level identifiers in data.store still vary by country — see order.completed → Country variations for country.code, currencyCode, and storeFiscalConfig.govIdType (CNPJ / CUIT / RUT / NIT / RUC / RIF) per country.

data.policy and data.lastKnown

Every order event carries these two blocks — not just this one. They were added together with the deferred-payment cycle and are added in v2.1, additive: existing consumers keep working unchanged.
Field-by-field detail lives in order.opened, the event where these blocks matter most.

Handler example

Common pitfalls

  • status === "CANCELLED", not paymentStatus. Cancelled paid orders keep paymentStatus === "SUCCEEDED"; the cancellation lives in the status field plus the cancellation block.
  • order.cancelled ≠ refund. Fire reports the cancellation; the refund (if any) is initiated by the source channel/processor and is not in this payload.
  • Don’t assume order.completed arrived first. Out-of-order delivery is possible — your handler should be tolerant to receiving order.cancelled for an orderId it doesn’t yet know about (e.g. log + create a placeholder; reconcile when order.completed arrives).
  • cancellation.metadata.fiscal is the original doc, not the cancellation result. For the SEFAZ confirmation, listen for order.reversed.

order.completed

The event you’ll see for the same order before cancellation.

order.reversed

Brazil only — fires when SEFAZ confirms the cancellation.

data.fiscalRepresentation

The fiscal numbering the point of sale obtained before injecting the order: it charges, requests the identifiers, prints the receipt, and only then injects. That is why it travels on the order and not in a separate fiscal event — by the time the order is born, this already happened.
The presence of this block does NOT mean the document is authorized. These are the numbers printed at the till; the tax authority’s verdict is in lastKnown.fiscal.status. A ticket that says “authorized” just because the block is present states something that may never have happened.
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. Carrying the block means numbering was attempted, not that it succeeded: numberingStatus tells you how the attempt ended, and failure why, when it did not end well. Branch on the value, not on the key’s presence:
The authority’s verdict does not alter it. What the customer took home printed does not change because the authority later authorizes or rejects — that is what lastKnown.fiscal is for, and that is what does move. What does replace it is a new document. The block carries the order’s current fiscal document. While there was only one, it was always the invoice; when a cancellation produces a credit note, the note is what sits on top — documentType says which one — and the invoice moves down into history, whole and with its own authority identifiers. It is not lost: it moves. compensates points at it by number, so the relationship stays explicit.

When numbering fails

A sale can be charged and end up with no fiscal receipt. That case travels too, and you must handle it: the identifiers come back null and the reason is in failure.
Branch on failure.scope:
  • TECHNICAL — print “pending” and carry on. It may resolve on its own.
  • FUNCTIONAL — some data is wrong and retrying will not fix it. Needs correction.
lastKnown.fiscal.sourceEvent now reports real provenance. It used to be derived from the status, so a processing seeded at injection was reported as fiscal.callback even though no callback had occurred. That case now says order.injected. If you branch on this field, account for the new value.