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

# store.business_day_closed

> A store closed its business day — a thin event with the close identity, timing, who closed it, and a minimal store ref.

<Tabs>
  <Tab title="v1 · current">
    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.
  </Tab>

  <Tab title="v0 · deprecated">
    <Warning>The **v0** contract (the full snapshot with `sales`/`metrics`/breakdowns embedded) is **deprecated** — kept only as a historical reference. Open it here: [store.business\_day\_closed — v0](/en/events-v0/store-day-closed).</Warning>
  </Tab>
</Tabs>

`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)

|                                  |                                                                                 |
| -------------------------------- | ------------------------------------------------------------------------------- |
| Coverage                         | **Universal** — all countries. No country gating.                               |
| Idempotency key                  | `event.id` (also `trigger.data.businessDayId`)                                  |
| 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`

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)

```json theme={null}
{
  "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

<ResponseField name="businessDayId" type="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.
</ResponseField>

<ResponseField name="businessDayDate" type="string">
  Calendar date the close represents, in the store's timezone (e.g. `"2026-03-31"`). **Not** the UTC date the event fired.
</ResponseField>

<ResponseField name="timezone" type="string">
  IANA timezone of the store (e.g. `"America/Sao_Paulo"`). Use it to interpret `businessDayDate`.
</ResponseField>

<ResponseField name="status" type="string">
  Always `"CLOSED"` for this event. No other value is possible here.
</ResponseField>

<ResponseField name="openedAt" type="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.
</ResponseField>

<ResponseField name="closedAt" type="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).
</ResponseField>

### `closedBy` — operator identity

<ResponseField name="closedBy" type="object">
  Already-resolved operator identity. **No runtime lookups needed downstream.**

  <Expandable title="closedBy">
    <ResponseField name="uid" type="string">
      For `type: "system"` (v1): the `accounts.id` UUID (or a stable nil UUID `"00000000-0000-0000-0000-000000000000"` for accounts without explicit system-user config). For `type: "user"` (v2, not yet emitted): the user's UUID from Fire's auth table.
    </ResponseField>

    <ResponseField name="name" type="string">
      Human-readable label. For system: typically `"Cierre Automático — {accountName}"`. For user: the user's display name.
    </ResponseField>

    <ResponseField name="type" type="string">
      `"system"` for automated cron-driven closes (v1). `"user"` is reserved for manual-close flows (v2, not yet implemented).
    </ResponseField>
  </Expandable>
</ResponseField>

### `store` — minimal store ref

<ResponseField name="store" type="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`](/en/events/order-completed#store) events for the same store.

  <Expandable title="store">
    <ResponseField name="uid" type="string | null">Fire's store UUID.</ResponseField>
    <ResponseField name="code" type="string | null">Human-readable store code (e.g. `"BR-SP-001"`).</ResponseField>
    <ResponseField name="externalId" type="string | null">The Fire-controlled external identifier (`settings.externalId`), not whatever any POS system injected. Use it as the partner-side reconciliation key.</ResponseField>
    <ResponseField name="countryCode" type="string | null">ISO country code of the store (e.g. `"BR"`).</ResponseField>
    <ResponseField name="timezone" type="string | null">IANA timezone of the store. Mirrors `data.timezone`.</ResponseField>
    <ResponseField name="currencyCode" type="string | null">ISO 4217 currency code of the store (e.g. `"BRL"`).</ResponseField>
  </Expandable>
</ResponseField>

## Handler example

```js theme={null}
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.

## Related events

<CardGroup cols={2}>
  <Card title="order.completed" icon="receipt" href="/en/events/order-completed">
    Fires per individual order as it completes. The full store block lives here.
  </Card>

  <Card title="order.cancelled" icon="circle-xmark" href="/en/events/order-cancelled">
    Per-cancellation event. Use it for cancellation detail instead of the day-close payload.
  </Card>
</CardGroup>
