Skip to main content
You’re viewing the current (v1) store.business_day_closed contract — a thin payload: close identity, timing, closedBy, and a minimal store ref. It no longer carries the day’s aggregate metrics, breakdowns, or anomaly detail — fetch those by businessDayId when you need them.
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 thin: it carries the close identity (businessDayId), timing, who closed it, and a minimal store ref — enough to react to “the day closed” and match it on your side. The heavy aggregates (sales, metrics, per-channel/payment breakdowns, force-closed and cancellation detail) live in the snapshot and are retrievable by businessDayId — they are not embedded in the event. Unlike order-level events, this is a store-level event: one per store closed, not one per order.

Trigger condition

Fire emits store.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 steps without error (force-close, metric calculation, snapshot persistence)
  • No snapshot already exists for that (storeId, businessDayDate) pair (idempotency guard)
CoverageUniversal — all countries. No country gating.
Idempotency keyevent.id (also trigger.data.businessDayId)
Fires more than onceNo, unless retried. One snapshot per (storeId, date).
SourceCron pipeline (v1). Manual close path is not yet emitting.
Latency relative to actual closeSynchronous — the event is enqueued immediately after saveSnapshot() returns.

What’s in trigger.data

A thin payload: identity (businessDayId, businessDayDate, timezone, status), timing (openedAt, closedAt), the resolved closedBy, and a minimal store ref (uid, code, externalId, countryCode, timezone, currencyCode). No metrics, breakdowns, or anomaly arrays — query the snapshot by businessDayId if you need the detail.

Example — real payload (sanitized)

{
  "event": {
    "id": "43214dd1-e260-4245-945d-5bbdfc4a54be",
    "type": "store.business_day_closed",
    "createdAt": "2026-05-18T21:59:01.306Z"
  },
  "data": {
    "businessDayId": "2c332116-79ec-4c63-a53d-8b87fff5a654",
    "businessDayDate": "2026-03-31",
    "timezone": "America/Sao_Paulo",
    "status": "CLOSED",
    "openedAt": "2026-03-31T09:00:02.55Z",
    "closedAt": "2026-05-18T21:58:09.882Z",
    "closedBy": {
      "uid": "00000000-0000-0000-0000-000000000000",
      "name": "Cierre Automático — Sandbox",
      "type": "system"
    },
    "store": {
      "uid": "a4019cad-bbac-4269-9f8d-f29654e92c45",
      "code": "BR-SP-001",
      "externalId": "200400500",
      "countryCode": "BR",
      "timezone": "America/Sao_Paulo",
      "currencyCode": "BRL"
    }
  },
  "_meta": { "executionId": "...", "flowId": "...", "flowName": "...", "attempt": 0, "triggerEntityId": "2c332116-79ec-4c63-a53d-8b87fff5a654" }
}

data.* reference

Identity & temporal

businessDayId
string
UUID of the persisted business-day snapshot 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 businessDayId — and as the lookup key to fetch the day’s aggregates from Fire’s snapshot API when you need them.
businessDayDate
string
Calendar date the close represents, in the store’s timezone (e.g. "2026-03-31"). Not the UTC date the event fired.
timezone
string
IANA timezone of the store (e.g. "America/Sao_Paulo"). Use it to interpret businessDayDate.
status
string
Always "CLOSED" for this event. No other value is possible here.
openedAt
string | null
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.
closedAt
string
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

closedBy
object
Already-resolved operator identity. No runtime lookups needed downstream.

store — minimal store ref

store
object
A minimal store reference — just identity plus country/timezone/currency. The full canonical store block (name, address, vendor, account, fiscal config) is not included here; if you need it, take it from the order.completed events for the same store.

Handler example

async function onStoreDayClosed(data) {
  const { businessDayId, businessDayDate, timezone, store, closedBy } = data;

  // 1. Idempotency — skip if we already processed this close
  const existing = await db.dayCloses.findUnique({ where: { businessDayId } });
  if (existing) return;

  // 2. Record the close (lean — identity + store ref)
  await db.dayCloses.create({
    data: {
      businessDayId,
      businessDayDate,
      timezone,
      storeExternalId: store.externalId,
      storeCode: store.code,
      countryCode: store.countryCode,
      currencyCode: store.currencyCode,
      closedByName: closedBy.name,
      closedByType: closedBy.type,
    },
  });

  // 3. Need the day's aggregates (sales, metrics, anomalies)? Fetch them by businessDayId.
  //    They are NOT in this event — query Fire's snapshot API on demand.
  // const snapshot = await fire.businessDay.get(businessDayId);
}

Common pitfalls

  • The day’s metrics are NOT in this event. sales, metrics, byChannel, byPaymentMethod, forceClosedOrders, closureStats, cancelledOrders and metadata no longer ship inline. If you relied on them, fetch the snapshot by businessDayId.
  • snapshotId was renamed to businessDayId. If you parsed data.snapshotId, switch to data.businessDayId (same value — the snapshot row id).
  • closedBy.uid may be the nil UUID "00000000-0000-0000-0000-000000000000" — for system closes where the account hasn’t configured a custom system user. Detect system closes via closedBy.type === "system", not by parsing uid.
  • businessDayDate is the store’s calendar date, not UTC. Interpret it with timezone.
  • store.* fields can be null for incomplete store configuration. Don’t assume non-null.

order.completed

Fires per individual order as it completes. The full store block lives here.

order.cancelled

Per-cancellation event. Use it for cancellation detail instead of the day-close payload.