- v0 · deprecated
- v1 · current
order.completed fires when an order is successfully injected and paid. It carries the V4 order snapshot as trigger.data — every field your flow needs to act on the order without calling back into Fire.
Trigger condition
Fire emitsorder.completed exactly once per order, the first time both of these are true at injection time:
order.status === "COMPLETED"order.paymentStatus === "SUCCEEDED"
PENDING payment, or that fail payment, never produce order.completed. Cancellations after completion produce a separate order.cancelled event — they do not retract order.completed.
| Coverage | Global (every country, every channel) |
| Idempotency key | event.id (= flow_executions.id) |
| Fires more than once | No, unless retried — use event.id to dedup |
| Ordering | Not guaranteed across orders — sort by data.createdAt if you need it |
| Retries | Up to 5 attempts with exponential backoff |
What’s in trigger.data
trigger.data is the V4 order snapshot — the same object that’s persisted in flow_queue.trigger_data and exposed to your flow’s templates.
The top-level keys, in the order they appear:
| Key | Type | Always present |
|---|---|---|
orderId | string (UUID) | yes |
orderCode | string | null | yes |
businessDayDate | string (YYYY-MM-DD) | yes |
externalOrderId | string | yes |
redeemPoints | boolean | yes |
accumulatePoints | boolean | yes |
discount | boolean | yes |
createdAt | string | null (ISO 8601 UTC) | yes |
orderComment | string (may be "") | yes |
paymentStatus | string (always "SUCCEEDED") | yes |
status | string (always "COMPLETED") | yes |
marketing | object | null | yes |
store | object | yes |
device | object | yes |
channel | object | yes |
operator | object | yes |
client | object | null | yes |
payments | object | yes |
kds | object | yes |
metadata | object (often {}) | yes |
orderLines | object[] | yes |
fulfillment | object | yes |
Example — real production payload (BR, sanitized)
The example below is taken from a realflow_queue.trigger_data row (Brazilian sandbox tenant, KIOSK channel, dine-in service). PII fields are replaced with placeholders; all other fields and shapes are verbatim.
Money values are strings expressed as integer minor units × 10000 (e.g.
"229000" = 22.9000 BRL). This avoids floating-point drift across multiple integrations. Parse with a decimal library, never parseFloat. The exception is paymentMethods[].totalBill, which the source sometimes ships as a JSON number — handle both.Field reference
Top-level identifiers
Fire’s internal order UUID. Stable across deliveries; use together with
event.id for traceability.Short, human-readable code shown on receipts and KDS displays (e.g.
95K, OC-br-001). null when the channel doesn’t assign one.Business day this order belongs to, in
YYYY-MM-DD. Computed in store-local time, so an order placed at 01:00 may belong to the previous business day depending on the store’s day-end cutoff.The order ID as provided by the channel/aggregator on injection. Use it when reconciling with upstream systems (POS, aggregator dashboards).
ISO 8601 UTC timestamp of when the order was originally placed. Distinct from
event.createdAt, which is when the flow execution started.Always
"COMPLETED" for this event.Always
"SUCCEEDED" for this event.true if loyalty points were redeemed on this order.true if the customer accumulated loyalty points.true if any discount was applied.Free-text customer note for the whole order. Empty string when not set.
data.store
Snapshot of the store at the moment the order was completed.
data.client
Customer who placed the order.
null for fully anonymous channel orders. For BR “consumidor final” orders, client is populated with placeholder values (govIdType: "FINAL_CONSUMER", govIdNumber: "00000000000").data.payments
Money breakdown.
data.fulfillment
How the order is being delivered.
data.kds
Kitchen-display context.
data.device and data.operator
{ uid, name, platform, metadata.ip } — originating device. Fields may be null for non-physical channels.{ uid, name, session.uid } — staff/cashier who processed the order. All fields null for self-service channels (kiosk, web).data.orderLines
Ordered products. Fully camelCase (transformed by the V4 builder).
data.marketing, data.metadata, data.channel
Loyalty + coupons.
null in most countries today; reserved for future use.Free-form bag for order-level extras. Often
{}.{ uid, code, metadata }. code examples: KIOSK, APP, IFOOD, RAPPI.Fiscal data
Fiscal data is included only when the store has fiscal emission enabled (
store.storeFiscalConfig.enabled === true). For non-fiscal countries or stores without configuration, all three locations below are absent or null.order.completed carries fiscal information in three distinct locations. Each serves a different purpose:
1. data.store.storeFiscalConfig — emitter identity & provider config
Identifies the legal entity emitting the document and how to authenticate with the fiscal provider. Credentials are intentionally NOT here — the fiscal node fetches them per provider/account.
2. data.payments.metadata.fiscal — order-level fiscal aggregates
SEFAZ-style aggregate totals, ready to be forwarded to a fiscal provider (your fiscal provider in Brazil). Values are strings × 10000.
3. data.orderLines[n].metadata.fiscal — per-line fiscal classification
Per-product fiscal codes. Used by the fiscal provider to classify each line on the document.
taxes[n].metadata (in payments.totals[].taxes[], orderLines[].price.totalPrice[].taxes[], and orderLines[].lineTotals[].taxes[]) with codes like cst, cBenef, cClassTrib, reducao, rateNominal, rateEffective.
Country variations
The example above is from a Brazilian store with fiscal emission enabled — the most complex case. The V4 shape is identical across countries; what changes is how much fiscal data is populated. Today, only Brazil carries the per-tax / per-line aggregates (payments.metadata.fiscal, orderLines[n].metadata.fiscal, lineTotals[n].taxes[]). Other countries have those blocks present but null / empty.
- Brazil (BR)
- Argentina (AR)
- Chile (CL)
- Colombia (CO)
- Ecuador (EC)
- Venezuela (VE)
Brazilian stores with
storeFiscalConfig.enabled === true carry the full fiscal payload — see the Fiscal data section above. Country markers:store.locationInfo.country.code: "BR"·name: "Brasil"·timezone: "America/Sao_Paulo"store.locationInfo.currencyCode: "BRL"store.storeFiscalConfig.govIdType: "CNPJ"(14 digits)store.storeFiscalConfig.secondaryGovIdType: "INSCRICAO_ESTADUAL"payments.totals[].currency_code: "BRL",paymentMethods[].currencyCode: "BRL",orderLines[].selectedCurrency: "BRL"- Populated:
payments.metadata.fiscal(vBC / vNF / vICMS / vPIS / vCOFINS / vTotTrib …),orderLines[].metadata.fiscal(ncm / cfop / csosn),lineTotals[].taxes[](ICMS, PIS, COFINS, IBS_*)
Quick reference table
| Country | country.code | Currency (effective) | govIdType (format) | Fiscal aggregates |
|---|---|---|---|---|
| Brazil | BR | BRL | CNPJ (14 digits) | Yes — full SEFAZ aggregates |
| Argentina | AR | ARS | CUIT (XX-XXXXXXXX-X) | No — null / empty |
| Chile | CL | CLP | RUT (XXXXXXXX-X) | No |
| Colombia | CO | COP | NIT (XXXXXXXXX-X) | No |
| Ecuador | EC | USD (Ecuador’s official currency) | RUC (13 digits) | No |
| Venezuela | VE | VES | RIF (J-XXXXXXXX-X) | No |
| Other countries | varies | varies | varies | No |
As more countries get a dedicated fiscal pipeline, their fiscal events will land as
fiscal.*.{cc} (e.g. fiscal.authorized.co, fiscal.authorized.ec). Until then, only order.completed and order.cancelled fire for non-BR stores — fiscal blocks remain null / empty.Handler example
Common pitfalls
- Decimals as strings × 10000.
payments.totals[0].total === "229000"means 22.9 BRL. Use a decimal library; neverparseFloat. - Casing is mixed in
payments.totals[]and parts ofpaymentMethods[]. Read bothcurrencyCodeandcurrency_codedefensively. The V4 builder transforms most of the snapshot but passes payment objects through. fulfillment.deliverymay be present even for non-delivery services with placeholder zeroes. Always branch onfulfillment.service.code.clientmay be a populated “FINAL_CONSUMER” placeholder in BR — it is notnull. TreatgovIdType === "FINAL_CONSUMER"as anonymous for analytics.event.idis the flow execution ID, not the order ID. Useevent.idfor idempotency (it changes per delivery), andorderIdfor business key.- Multi-tenant routing. Use
store.account.uid,store.vendor.uid, andstore.codeto route to the right tenant in your system, even though Fire already scopes the flow on its side.
Related events
order.cancelled
Fires when this order is later cancelled.
order.invoiced
Brazil only — fires once SEFAZ authorizes the order’s fiscal document.

