> ## Documentation Index
> Fetch the complete documentation index at: https://docs.zeotap.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Calculated Attributes: Use Cases

> Six proven Calculated Attribute patterns — from lifetime value to B2B account activity — with the exact operator, window, and activation rule for each.

A good Calculated Attribute (CA) use case always has the same shape: your users generate scattered events — purchases, sessions, page views, logins — and your campaigns need **one decision-ready number per customer**. A CA collapses that event stream into a single per-profile value that Zeotap keeps refreshed for you, daily for batch CAs or immediately for real-time CAs. Once the attribute is **Active**, anyone on the team can use it in an audience rule such as `sum_order_value_90d > 500` — no SQL, no data-team ticket.

The six scenarios below are the canonical patterns. Each one shows the customer problem, the CA design (type, operator, window), how to plug it into activation, and the pitfall to avoid.

## Lifetime Value tracking (retail)

A retailer takes orders on the web, in the app, and in stores. The revenue for one customer is scattered across dozens of purchase events, so the campaign team cannot answer the simplest question: *how much has this person spent with us recently?* Spend tiers, loyalty eligibility, and lookalike seeds all need that one number.

| Attribute name        | Type  | Operator | Field       | Events   | Window       | Group by |
| --------------------- | ----- | -------- | ----------- | -------- | ------------ | -------- |
| `sum_order_value_90d` | Batch | Sum      | Order Value | Purchase | Last 90 days | —        |

In the sentence builder this reads: *For each user in my database, calculate a new attribute as: **Sum** of **Order Value** field, when they performed **Purchase**.* If you also need the split by region or brand, add a **Group By** (batch only, up to 3 fields) — one value per country per customer instead of a single overall value per customer.

**Plug it into activation:** build a *High Spenders* audience with the rule `sum_order_value_90d > 500` and route it to a premium journey or a paid-media destination. For **instant** loyalty rewards, add a real-time variant — a Real-Time CA using **Sum** of Order Value over a short window — and trigger the reward the moment cumulative spend crosses €500. Real-Time CAs update as events arrive and are enabled per account — ask your Zeotap representative if the option is not visible in your workspace.

<Frame caption="A fully configured Real-Time Calculated Attribute: Sum of Order Value over the last 24 Hours.">
  <img src="https://mintcdn.com/zeotap/DjvOawYrjChNZWhK/articles/unify-customer/Storage/unify-customer/calculated-attributes-use-cases/ca-rt-form-complete.png?fit=max&auto=format&n=DjvOawYrjChNZWhK&q=85&s=4396e47ef2747119aa028bfbc0774999" alt="Real-time Calculated Attribute form configured with the Sum operator on the Order Value field over the last 24 hours" width="666" height="720" data-path="articles/unify-customer/Storage/unify-customer/calculated-attributes-use-cases/ca-rt-form-complete.png" />
</Frame>

<Tip>
  The time window is fixed when you create a CA, so teams typically run parallel 30/60/90-day variants named consistently (`sum_order_value_30d`, `_60d`, `_90d`). For the allowance and lifecycle implications, see [Calculated Attributes: Guardrails & Limits](/articles/unify-customer/calculated-attributes-guardrails-and-limits).
</Tip>

## Churn / re-engagement signal (subscription)

A subscription service rarely sees churn announce itself — customers simply stop logging in weeks before they cancel. The retention team needs a signal that says "this account was active recently, but has gone quiet", early enough for a win-back offer to land.

The classic design uses **two Count CAs over nested windows**. Count aggregates the events themselves, so no field is needed:

| Attribute name | Type  | Operator | Field | Events        | Window       | Group by |
| -------------- | ----- | -------- | ----- | ------------- | ------------ | -------- |
| `sessions_60d` | Batch | Count    | —     | Session Start | Last 60 days | —        |
| `sessions_30d` | Batch | Count    | —     | Session Start | Last 30 days | —        |

In the sentence builder each reads: *For each user in my database, calculate a new attribute as: **Count**, when they performed **Session Start*** — once with a 60-day window and once with a 30-day window.

**Plug it into activation:** define a *Lapsing* audience with `sessions_60d` **Greater** `0` AND `sessions_30d` **Does Not Exist** — active in the last two months, silent in the last one — and feed it into a win-back journey with an incentive. This is the same two-CA pattern Zeotap documents for monthly repurchase campaigns in retail.

<Warning>
  Don't write the second half of that rule as `sessions_30d = 0`. A user with **no** qualifying events in the window carries **no value at all** for the CA — not a `0` — so an `= 0` rule silently matches nobody. The audience builder's operator list for calculated attributes includes **Exists** and **Does Not Exist** for exactly this case. Before launching, sanity-check the audience count against your expectation. See [Calculated Attributes: Guardrails & Limits](/articles/unify-customer/calculated-attributes-guardrails-and-limits#users-with-no-qualifying-events) → Users with no qualifying events.
</Warning>

<Frame caption="The audience builder: a rule on a calculated attribute, with the operator list including Exists and Does Not Exist.">
  <img src="https://mintcdn.com/zeotap/DjvOawYrjChNZWhK/articles/unify-customer/Storage/unify-customer/calculated-attributes-use-cases/ca-audience-operators.png?fit=max&auto=format&n=DjvOawYrjChNZWhK&q=85&s=6fbc9e0ae6bcc8ac3ff95002e8180725" alt="Audience builder rule block on a calculated attribute with the operator dropdown open, showing operators including Greater Or Equal, Less Or Equal, Greater, Less, Equal, Not Equal, In, Not In, Exists, and Does Not Exist" width="1040" height="552" data-path="articles/unify-customer/Storage/unify-customer/calculated-attributes-use-cases/ca-audience-operators.png" />
</Frame>

<Warning>
  Batch CAs refresh once a day — see [Calculated Attributes: Guardrails & Limits](/articles/unify-customer/calculated-attributes-guardrails-and-limits) for freshness expectations — so position batch-driven win-back as a next-day play; if you need a same-session reaction, use a real-time CA instead.
</Warning>

## Favourite category (retail/content)

A retailer wants the homepage, newsletter, and push notifications to lead with each customer's favourite category — running gear for one shopper, home decor for another. The raw signal is there in purchase events; it just needs to be reduced to one value per profile.

| Attribute name           | Type  | Operator      | Field            | Events   | Window       | Group by |
| ------------------------ | ----- | ------------- | ---------------- | -------- | ------------ | -------- |
| `favourite_category_90d` | Batch | Most frequent | Product Category | Purchase | Last 90 days | —        |

In the sentence builder this reads: *For each user in my database, calculate a new attribute as: **Most frequent** of **Product Category** field, when they performed **Purchase**.* If several categories tie, a single value is returned deterministically. When you need the **top 3** categories rather than a single winner, Standard mode can't express it — switch the Definition Type to **SQL** (the SQL CA Editor is Beta and enabled per account on request; see [Create a Batch Calculated Attribute](/articles/unify-customer/add-a-calculated-attribute)) and use the top-N pattern from the in-product Help Guide:

```sql theme={null}
SELECT
  ucid,
  ARRAY_AGG(STRUCT(category, cnt) ORDER BY cnt DESC LIMIT 3) AS top_categories_90d
FROM (
  SELECT ucid, category, COUNT(*) AS cnt
  FROM %%EVENT_STORE%%
  WHERE event_name = 'purchase'
    AND date(_zeotap_timestamp) >= CURRENT_DATE() - 90
  GROUP BY ucid, category
)
GROUP BY ucid
```

A related design is per-category spend via **Group By** — the same mechanic shown below splitting Sum of Order Value by Country works for any grouping dimension:

<Frame caption="Group By splits one batch CA into one value per group — here Sum of Order Value grouped by Country.">
  <img src="https://mintcdn.com/zeotap/DjvOawYrjChNZWhK/articles/unify-customer/Storage/unify-customer/calculated-attributes-use-cases/ca-batch-groupby.png?fit=max&auto=format&n=DjvOawYrjChNZWhK&q=85&s=f7333633c9538640a6a09735913d68e9" alt="Completed batch Calculated Attribute form: Sum of Order Value over the last 90 days, grouped by Country" width="666" height="595" data-path="articles/unify-customer/Storage/unify-customer/calculated-attributes-use-cases/ca-batch-groupby.png" />
</Frame>

With Group By applied, each profile carries one value **per group**, stored as a **list-of-objects** attribute: each entry holds the computed value alongside its group-by attribute values — for example, one entry per country with that country's order value. You can use and filter it directly in audiences and journeys, targeting any single group — for example, customers whose order value *for one specific country* exceeds a threshold.

**Plug it into activation:** an audience rule like `favourite_category_90d = "Running"` drives category-led newsletters and on-site personalisation.

<Tip>
  Group By only offers attributes at the same level as the field you aggregate — if a grouping field is missing from the picker, it is likely at a different level than the aggregation field — see [How Calculated Attributes Work](/articles/unify-customer/how-calculated-attributes-work).
</Tip>

## Engagement scoring (media/publishing)

A media company needs to know which readers are engaged before deciding who sees the paywall, who gets the retention newsletter, and who is at risk of going dormant. Page views land as thousands of individual events; the editorial and CRM teams need simple engagement bands.

| Attribute name      | Type  | Operator           | Field           | Events    | Window       | Group by |
| ------------------- | ----- | ------------------ | --------------- | --------- | ------------ | -------- |
| `page_views_7d`     | Batch | Count              | —               | Page View | Last 7 days  | —        |
| `sections_read_30d` | Batch | Unique Value Count | Content Section | Page View | Last 30 days | —        |

In the sentence builder the first reads: *For each user in my database, calculate a new attribute as: **Count**, when they performed **Page View**.* The second uses **Unique Value Count** of **Content Section** on the same event.

The first CA measures volume, the second breadth — a reader who visits daily but only reads one section behaves differently from one who roams the whole site.

**Plug it into activation:** `page_views_7d` **Less** `5` **OR** `page_views_7d` **Does Not Exist** defines a *Low Engagement* audience for a re-engagement push — the second clause matters, because a reader with no page views at all carries no value for the CA and would slip past a `< 5` rule on its own. `page_views_7d >= 5 AND sections_read_30d >= 3` marks broad, habitual readers who are strong subscription-upsell candidates.

**Real-time variant:** to react while an engaged reader is still on the site — for example, surfacing a subscription offer mid-session — create a Real-Time CA using **Count** of Page View events over the last few **Hours** and trigger the moment a threshold is crossed.

<Tip>
  A newly created CA shows **In Progress** until its first computation completes, and only an **Active** CA can be used in workflows. Create your scoring attributes ahead of campaign day, not on it.
</Tip>

## Recency signals

Across industries, *how long since the last action* is one of the strongest predictors of churn — and one of the most useful suppression signals. A marketplace, for example, wants to stop paying to re-acquire someone who bought yesterday, and to nudge someone drifting past 60 days.

| Attribute name     | Type  | Operator       | Field              | Events   | Window           | Group by |
| ------------------ | ----- | -------------- | ------------------ | -------- | ---------------- | -------- |
| `last_purchase_ts` | Batch | Last Timestamp | Purchase Timestamp | Purchase | Over entire data | —        |

In the sentence builder this reads: *For each user in my database, calculate a new attribute as: **Last Timestamp** of **Purchase Timestamp** field, when they performed **Purchase**.*

Audience rules can work with timestamps directly (for example, last purchase within the last 14 days). If you want recency as a plain **number of days** — easier for banding and downstream tools — Standard mode can't compute a difference, so use a SQL CA following the in-product days-since-last-event pattern (event timestamps are epoch milliseconds, hence the `TIMESTAMP_MILLIS()` wrapper — the in-product Help Guide on [Create a Batch Calculated Attribute](/articles/unify-customer/add-a-calculated-attribute) covers the exact columns and functions):

```sql theme={null}
SELECT
  ucid,
  TIMESTAMP_DIFF(CURRENT_TIMESTAMP(),
                 TIMESTAMP_MILLIS(MAX(event_timestamp)), DAY) AS days_since_last_purchase
FROM %%EVENT_STORE%%
WHERE event_name = 'purchase'
GROUP BY ucid
```

**Plug it into activation:** `days_since_last_purchase >= 30 AND days_since_last_purchase <= 90` feeds a win-back journey; `last_purchase_ts` within 14 days suppresses recent buyers from acquisition campaigns.

**Real-time variant:** batch recency lags by up to a day, so for same-day suppression add a Real-Time CA using **Count** of Purchase events over the last 24 **Hours** — suppress anyone where that attribute **Exists** (any purchase today), and keep the batch CAs for the longer-window bands.

<Warning>
  If you use a **real-time** CA with a short lookback for journey suppression, a delayed event can slip past the window — the CA no longer reflects the purchase even though it happened. Align the CA window with the longest delay step in your journey.
</Warning>

## B2B account activity

A B2B software provider has a mismatch: product activity (logins, orders, feature usage) happens at the **account** level, but marketing and CSM outreach targets individual **contacts**. The team wants each contact's profile to carry usage signals so renewals and nurture flows can react to quiet accounts.

| Attribute name | Type  | Operator | Field | Events | Window       | Group by |
| -------------- | ----- | -------- | ----- | ------ | ------------ | -------- |
| `logins_30d`   | Batch | Count    | —     | Login  | Last 30 days | —        |

For account-level context, model accounts as a non-customer-entity (NCE) source joined to your contacts. In SQL mode, the in-product Help Guide exposes that data through the `%%NCE_STORE_<SOURCE_ID>%%` macro table (replace `<SOURCE_ID>` with your actual source ID), letting a SQL CA combine event activity with account attributes. The exact fields depend on how your account source is mapped in the Catalogue.

<Note>
  CAs are computed **per unified customer profile** — the pattern here attaches activity and account context to *each contact's* profile rather than rolling one value up across all contacts of an account. For account-level rollup requirements, contact your Zeotap representative.
</Note>

In the sentence builder this reads: *For each user in my database, calculate a new attribute as: **Count**, when they performed **Login**.*

**Plug it into activation:** an audience of contacts where `logins_30d` **Does Not Exist** (no logins recorded in the window — remember, no qualifying events means no value, not `0`) at accounts flagged as in-renewal (from the joined account data) can trigger a CSM alert journey or a re-onboarding email sequence.

<Tip>
  When joined NCE data lands as List-type attributes, audience operators apply ANY/ALL semantics — see [Apply Attributes to Your Audience](/articles/segment-customer/apply-attributes-to-your-audience) before building rules on them.
</Tip>

## Related Topics

For the concept layer, see the companion guide [How Calculated Attributes Work](/articles/unify-customer/how-calculated-attributes-work); for every limit and guardrail in one place, see [Calculated Attributes: Guardrails & Limits](/articles/unify-customer/calculated-attributes-guardrails-and-limits).

<CardGroup cols={2}>
  <Card title="How Calculated Attributes Work" href="/articles/unify-customer/how-calculated-attributes-work" icon="angles-right" iconType="solid" horizontal={true} />

  <Card title="Calculated Attributes: Guardrails & Limits" href="/articles/unify-customer/calculated-attributes-guardrails-and-limits" icon="angles-right" iconType="solid" horizontal={true} />

  <Card title="Create a Batch Calculated Attribute" href="/articles/unify-customer/add-a-calculated-attribute" icon="angles-right" iconType="solid" horizontal={true} />

  <Card title="Create a Real-Time Calculated Attribute" href="/articles/unify-customer/create-a-real-time-calculated-attribute" icon="angles-right" iconType="solid" horizontal={true} />

  <Card title="Batch vs Real-Time Calculated Attributes" href="/articles/unify-customer/batch-vs-real-time-calculated-attributes" icon="angles-right" iconType="solid" horizontal={true} />

  <Card title="Apply Attributes to Your Audience" href="/articles/segment-customer/apply-attributes-to-your-audience" icon="angles-right" iconType="solid" horizontal={true} />
</CardGroup>
