store.business_day_closed fires when Fire’s automated business-day-close pipeline finishes building the immutable snapshot for a store’s operational day. The payload is a direct projection of that snapshot plus the canonical v4 store block — so consumers (Kinesis, ERP, BI) can construct their own shape with zero queries back to Fire.
Unlike order-level events, this is a store-level event: one per store closed, not one per order. The snapshot contains aggregate metrics, per-channel/payment breakdowns, force-closed order detail, and cancellation detail for the day.
Trigger condition
Fire emitsstore.business_day_closed once per (storeId, businessDayDate) snapshot, the first time all of these are true:
- The store had an open business day (
isBusinessDayOpen === true) for the date being closed - The close pipeline (
CloseBusinessDayService.execute()) completed all 9 steps without error (force-close, metric calculation, snapshot persistence) - No snapshot already exists for that
(storeId, businessDayDate)pair (idempotency guard)
| Coverage | Universal — all countries. Unlike fiscal.*.br, no country gating. |
| Idempotency key | event.id (also trigger.data.snapshotId) |
| Fires more than once | No, unless retried. One snapshot per (storeId, date). |
| Source | Cron pipeline (v1). Manual close path is not yet emitting. |
| Latency relative to actual close | Synchronous — the event is enqueued immediately after saveSnapshot() returns. |
What’s in trigger.data
The full V4StoreDayClosedSnapshot — 17 top-level keys covering identity, the canonical store block, aggregate sales/metrics/summary, per-channel and per-payment-method breakdowns, force-closed order detail, cancelled order detail, and extensible metadata.
Example — real payload (sanitized)
data.* reference
Identity & temporal
UUID of the persisted snapshot row in Fire’s database. Also surfaced as
_meta.triggerEntityId. Use it as the idempotency key on your side — re-deliveries of the same close share the same snapshotId.Calendar date the close represents, in the store’s timezone (e.g.
"2026-03-31"). Not the UTC date the event fired.IANA timezone of the store (e.g.
"America/Sao_Paulo"). Use it to interpret businessDayDate and the metrics.*_order_at timestamps.Always
"CLOSED" for this event. The field exists to keep parity with the snapshot row shape; no other value is possible here.ISO 8601 UTC timestamp the business day opened. May be
null for stores that never explicitly opened (legacy data); for v1 cron-closed stores it’s always present.ISO 8601 UTC timestamp the close pipeline persisted the snapshot. Use this for SLI/SLO measurements rather than
event.createdAt (which is the queue dispatch time).closedBy — operator identity
Already-resolved operator identity. No runtime lookups needed downstream.
store — canonical store block
Same shape as in order.completed — see order.completed for the field reference. Key callouts:
store.externalIdis the Fire-controlled identifier fromsettings.externalId, not whatever any POS system happened to inject. Use it as the partner-side reconciliation key.store.storeFiscalConfigtravels complete (CNPJ, IE,storeCode3S, etc. for BR; analogous fields for other countries). Credentials are never included.store.vendor.nameandstore.account.nameare populated from Fire’s account metadata.descriptionandloyaltyPlanmay benull/falsehere even when populated in order events — those fields come from the injected POS payload in order events and are not part of Fire’s account metadata today.
summary, sales, metrics
Order count breakdown for the day.
Revenue aggregates. Includes only
COMPLETED + SUCCEEDED orders in the dominant currency.Performance indicators.
Breakdowns
Sales by channel (APP, KIOSK, POS, WEB, …). Each entry includes a
by_fulfillment sub-breakdown (DELIVERY, PICKUP, DINE_IN, etc.). Only COMPLETED + SUCCEEDED orders.Sales by payment processor (
CASH, CARD, PIX, …). Aggregated from payment_methods[].processor across paid orders.Anomalies
Detail of orders that were still
OPEN at close time. Each entry includes order_id, order_uid, payment_status, total, the original payment_methods[] snapshot, and a closure_reason classifying why the order didn’t complete naturally.Possible closure_reason values: ABANDONED, PAYMENT_PENDING, PAYMENT_FAILED, PARTIAL_PAYMENT, INTEGRATION_ERROR.Aggregate force-close stats — count per reason and total uncollected revenue (sum of
forceClosedOrders[].total).Per-order cancellation detail (
order_id, order_uid, order_code, cancelled_at, cancellation_reason, cancellation_source of "adapter" or "backoffice", total, currency). For snapshots created before this field existed (legacy data), this array is [].Extensible bucket. Today carries
cash_reconciliation_summary (always, even if empty) and optionally currency_anomaly (when more than one currency was detected in the day). May grow over time without breaking the contract — treat unknown keys as forward-compatible.Handler example
Common pitfalls
closedBy.uidmay be the nil UUID"00000000-0000-0000-0000-000000000000"— for system closes where the account hasn’t configured a custom system user. UseclosedBy.type === "system"to detect system closes rather than parsinguid.sales.currencycan benullwhen the store closed without any paid orders. Don’t assume a default — branch on the null.closureStats.by_reasonkeys are always present, even when zero. Don’t filter for presence; sum/compare on the values.metrics.first_order_at/last_order_at/peak_hour*are absent (notnull, absent) when the store had no orders. Use optional chaining.cancelledOrdersis[]for snapshots from before the per-order cancellation detail was added. If you need it on historical data, query Fire’s snapshot API directly — older snapshots are not retroactively backfilled.store.vendor.descriptionandstore.vendor.loyaltyPlanmay benull/falsehere even when populated in order-level events. These fields originate from the POS-injected payload in order events and aren’t stored in Fire’s account metadata today.
Related events
order.completed
Fires per individual order as it completes.
store.business_day_closed aggregates many order.completed events.order.cancelled
Per-cancellation event. Each cancellation also appears in
cancelledOrders[] of the day’s close.
