> ## Documentation Index
> Fetch the complete documentation index at: https://help.the-meridian.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Usage view crosses a threshold

**Usage view crosses a threshold** (`usage_view.threshold_crossed`) fires the first time a store's value for one of your [usage views](/usage-views) reaches a number you set. Where [Custom event is tracked](/usage-event-tracked) reacts to *an* event, this one reacts to *the total*: "this store's GMV just passed 10,000 this billing period". It carries the event that took it there too, so a milestone email can print both.

## Configuring it

Two settings, and both are required:

* **Usage view**: which view to read. The editor prints the view's definition and window under the picker, because "crosses 10,000" means nothing until you know whether the figure resets every billing period or counts everything ever reported.
* **Threshold**: an absolute number, in the view's own unit. 10,000 of `properties.total_price` is 10,000 of whatever currency your app sends.

There is no "any view" option, unlike the custom-field and custom-event triggers: one number cannot be about several views that measure different things over different windows.

<Note>
  The threshold is an absolute value, not a percentage of a plan's included quantity. Pricing a view is [plan terms](/create-a-plan); this trigger is about the figure itself.
</Note>

## When it fires

On the **transition**, and only upward. Meridian evaluates the view when your app tracks an event that moves it: if the store's value goes from *below* the threshold to *at or above* it, the flow runs for that store.

Everything follows from that:

* A store already above the number when you create the automation does **not** fire. It never transitions across the line; the next store to reach it does.
* Lowering the threshold later does not fire for the stores already past the new number, for the same reason.
* An event your view [skips](/usage-views) (a total over an event property the event didn't carry) moves nothing, so it can't cross anything.
* An event dated outside the view's **current window** moves nothing either. An order your app [dated](/events#dated-events) in September does not count toward an October month-to-date view, nor toward a billing-period view once that period has rolled over, so it cannot take either across a threshold. An all-time view has no window edge, so every event moves it.

## Once per store, per window

A busy store can cross a threshold and then keep going. The flow runs **once**, and when it may run again depends on the view's window:

| Window | Runs again |
| - | - |
| **Billing period**, **Month to date** | In the store's **next** window. A value that dips and re-crosses inside one period does not send a second message. |
| **Last 7 days**, **Last 30 days**, **All time** | Once the store's value has fallen **back under** the threshold and crossed it again. These windows have no boundary to reset on, so a genuine round trip under the line is what re-arms them. |

A value can fall for two reasons: a negative contribution (a refund summed into a total), or, on a rolling window, events sliding out of it. Either way the re-arm is noticed at the next event that moves the view. Nothing is evaluated on a schedule.

Two automations watching the same view are two independent promises: each gets its own run.

## Live usage only

A threshold is only ever tested on **live ingestion**: a `track()` call that recorded a new event. Recomputing a view, backfilling its history, or re-seeding its counters rewrites the very numbers this trigger reads, and none of them send anything. An SDK retry of the same call (same idempotency key) doesn't either: it records no new event.

This is deliberate. A recompute can take thousands of stores from below a threshold to above it at once, for data that is months old. That is a maintenance operation, not ten thousand milestones.

An event your app [dated](/events#dated-events) more than **24 hours** before Meridian received it is a backfill sent through the live call, and it is treated like one: it is recorded, it moves the view, and it is never tested against a threshold. It becomes part of where the store stood before the next live event, which is then measured correctly. So a store whose replayed history already takes it past 10,000 gets no milestone for it, and its next live order does not cross anything either, because the store was already above the line. The cost: a milestone reached only by events that arrive more than 24 hours late is never sent.

In a batch that mixes the two, only the recent events inside the view's window count toward the crossing, and the event the flow carries is always one of them.

## What it carries

The store's [variables](/automation-variables), plus the crossing itself:

| Variable | Example | Notes |
| - | - | - |
| `usage_view_key` | `gmv` | The view's key, what `getUsage()` takes |
| `usage_view_name` | `GMV` | The view's name in Meridian |
| `usage_view_event_key` | `orders_synced` | The event the view reads |
| `usage_view_value` | `10450.5` | The store's value at the moment it crossed |
| `usage_view_threshold` | `10000` | The number you configured |
| `usage_view_window` | `billing_period` | The view's window id |
| `usage_view_window_start` | `2026-08-03` | UTC date the window opened |
| `usage_view_window_end` | `2026-09-02` | UTC date it closes, exclusive |

On an `all_time` view the two bounds are **absent** (the window is unbounded), so they render as nothing and satisfy only **is empty** in a [condition](/condition-variable).

### The event that crossed

The trigger also carries the event that took the view over the line, under the same names [Custom event is tracked](/usage-event-tracked) uses:

| Variable | Example | Notes |
| - | - | - |
| `event_key` | `order_created` | The key your app tracked |
| `event_name` | `Order created` | Its name in your event catalog, falling back to the key |
| `event_quantity` | `1` | The quantity that `track()` call reported |
| `properties.<name>` | `1000` | Every scalar [property](/automation-variables#event-properties) the event carried |

So "your 1000th order" can name the order:

```html theme={null}
<p>{{store_name}} just passed {{usage_view_threshold}} orders.</p>
<p>Order #{{properties.order_number}} came to
{{properties.order_amount}} {{properties.currency}}.</p>
```

The properties follow the same rules as on the tracked trigger: scalars only, up to three levels, absent rather than guessed, and case-sensitive. See [Event properties](/automation-variables#event-properties).

### Which event, when a batch crosses

A [batch](/events#sending-events-in-batch) is still one transition, so one event out of it is the one the flow carries, and it is the exact one: Meridian walks the batch in the order your app sent it, starting from the store's value before the call, and carries the event at which the running total **reached the threshold**.

So a store on 0 orders that reports three in one `trackMany` call crosses a threshold of 1 on the **first** of them, and a "first order" email is about the first order.

This is decided per automation, not per batch, because the number is the automation's. Two flows on the same view, one at 1 and one at 100, fed by a single call that takes a store from 0 to 150, run once each and carry a different order: the 1st and the 100th.

An event the view [skips](/usage-views) counted nothing, so it is never carried and never counted on the way. Landing exactly on the threshold counts as reaching it, the same **at or above** test that decides whether the flow runs at all.

For a single `track()` call, which is the ordinary case, there is nothing to choose: that event is the one.

## Building on it

* Congratulate a store on its first 10,000 of processed GMV, once per cycle.
* Send a "first order" email on a count view with a threshold of 1, printing the order itself from its `properties.`. The tracked trigger cannot express "first", because it has no variable for the store's running count.
* Warn a store approaching a limit your app enforces itself, before it hits it.
* Write the **reading itself** into a [custom field](/populate-custom-field) (the action can store `usage_view_value` rather than a fixed marker), so [segments](/segments) and [reports](/report-builder) can work off the number, and let your sales follow-up run from there.

<Tip>
  Pair it with a [variable condition](/condition-variable) on `usage_view_value` when one flow serves several tiers: one trigger at the lowest threshold, branches above it.
</Tip>


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.