Skip to main content
See also: Behavioral Metrics REST API reference for request/response shapes, status codes, and error semantics.

Overview

Behavioral metrics let you define computed aggregations over a customer’s interaction history. They transform raw event data (impressions, clicks, conversions) into meaningful signals like “email frequency in the last 7 days” or “conversion rate for credit card offers.” You use these metrics to power dynamic rules in contact policies, qualification, and scoring.
Behavioral Metrics is the platform’s single behavioral-aggregation surface. The older Summary Definitions feature has been retired — it was never wired to an aggregation engine. Behavioral Metrics materializes real per-customer MetricValue rows (realtime or batch) and is what Decisioning Gates actually read via the metric_condition rule type.

Field Reference

Aggregation Functions

The ratio aggregation function requires a secondary event type. For example, a click-through rate metric would use positive (clicks) as the source field and compute the ratio against impressions.

Source Fields

The sourceField determines which column of the outcome/interaction record is aggregated:

Dimensions

Dimensions control how the metric is grouped. You can specify up to 4 dimensions, mixing the built-in fixed dimensions with any custom dimension registered for your tenant: Examples:
  • No dimensions: “Total clicks in 7 days” (single number per customer)
  • channelId dimension: “Clicks per channel in 7 days” (one number per channel per customer)
  • channelId + offerId dimensions: “Clicks per channel per offer in 7 days”
  • channelId + a custom campaign dimension: “Clicks per channel per marketing campaign in 7 days”

Custom dimensions

Beyond the four fixed dimensions above, a metric can group by any dimension defined in your tenant’s Dimensions registry (/data/dimensions) — things like a marketing campaign, a customer’s region, or a journey stage. Each registered dimension has a source that tells the platform where to read its value from: See Dimensions for the full registry field reference and worked examples for each source.

Capture

Custom dimensions are resolved and stamped onto interaction_history.dimensions (JSONB) at every point an interaction row is written: /api/v1/recommend, /api/v1/respond, /api/v1/respond/bulk, the pipeline’s outcomes node (third-party response-file ingestion), and batch campaign delivery. Capture is best-effort and never blocks or fails the write it’s attached to — if dimension resolution errors for any reason, the row is written with no dimensions rather than failing the request. Not every write point has the same context available (e.g. a categoryId decision-dimension only resolves at /recommend; request_attribute dimensions don’t resolve on batch campaign delivery) — see the full capture matrix in Dimensions → Capture.

How custom-dimension metrics compute

A metric that groups by only the four fixed dimensions computes the same way it always has — from the interaction_summaries daily rollups. A metric that groups by any custom dimension key instead computes with a windowed GROUP BY directly over interaction_history, since custom dimension values only exist there (the daily summary rollups have no room for tenant-defined columns).
avg/min/max on custom-dimension metrics use daily grain, matching the fixed-dimension path: history rows are first pre-aggregated to one value per (customer, dimension combination, day), and only then is avg/min/max applied across days. This keeps a custom-dimension metric’s semantics consistent with a fixed-dimension metric computed over the same data — without it, avg/min/max would run per-event instead of per-day and the two paths would silently diverge. count, sum, and ratio are exact (no pre-aggregation step).

Automatic re-aggregation

Because a custom-dimension metric always recomputes from the interaction_history fact table rather than a fixed rollup, adding, removing, or changing a dimension is fully reflected across the metric’s history-in-window the next time it recomputes — there’s no stale precomputed shape to migrate. Recompute happens automatically in three ways, on top of the compute modes described above:
  • On save — creating or modifying a batch metric fires an async recompute immediately (fire-and-forget; a failure is logged, not surfaced, so a slow or failing recompute can never fail the save).
  • On schedule — the same recompute-metrics cron described in Batch Mode recomputes custom-dimension metrics that are due, exactly like fixed-dimension ones.
  • On demand — the “Compute Now” action in the UI (POST /api/v1/behavioral-metrics/{id}/compute).

History honesty

Whether a custom dimension has a value for an older interaction depends on its source:
  • request_attribute, decision, and outcome dimensions can be backfilled onto existing interaction_history rows, because their source values (request context, offer/channel/creative/category, outcome key) are already stored verbatim on the row. A one-time backfill script is provided — see Dimensions → Backfilling history.
  • customer_attribute dimensions are forward-only: the customer’s profile is read live at capture time and isn’t stored verbatim on the interaction row, so there’s nothing to backfill. It resolves wherever that live profile lookup happens — /api/v1/recommend, /api/v1/respond/bulk, batch campaign delivery, and the pipeline outcomes node — but not on plain /api/v1/respond, which doesn’t load the customer profile on that path.
  • journey dimensions are also forward-only, and currently capture no value anywhere: the journey engine doesn’t write to interaction_history yet, so a journey-sourced dimension shows (unset) on every interaction today, not just older ones. The source exists so journey-stage dimensions work once journeys write to interaction history.
  • Interactions recorded before a customer_attribute/journey dimension had a working capture path (or before a backfill was run, for the backfillable sources) show as (unset) for that dimension in the UI and are grouped under an empty value at compute time.

Realtime is not supported for custom dimensions

Creating or updating a metric with computeMode: "realtime" and a custom dimension in groupByDimensions is rejected with 400. The realtime increment path updates a value in place from a fixed interaction context that has no custom-dimension values, so a realtime metric grouped by a custom key would silently accumulate everything under one key forever. Use computeMode: "batch" for any metric that groups by a custom dimension.

Unregistered dimension keys

groupByDimensions accepts arbitrary strings at save time (a tenant’s custom keys can’t be enumerated in a static schema), so creating a metric with a typo’d or not-yet-registered custom key is not rejected at save. It fails at compute time instead: the history compute path validates every non-fixed key against the tenant’s registered Dimensions, and an unregistered or invalid key returns 400 from the manual POST /api/v1/behavioral-metrics/{id}/compute endpoint. (The on-save and scheduled recomputes hit the same validation but only log the failure, since they run in the background rather than in response to a request.)

Time Window

The windowDays parameter defines the rolling lookback period:
  • Minimum: 1 day
  • Maximum: 3650 days (custom windows are supported — not just the preset periods used elsewhere in the platform)
  • Set to null or omit for an all-time aggregation
  • The window is always rolling — it looks back from the current moment
Behavioral metrics aggregate from the daily interaction-summary rollups, which are purged after summaryRetentionDays (see Retention). A windowDays longer than your tenant’s configured summary retention period will return partial data — the source rows for the older part of the window have already been purged. Keep long-window metrics within your configured retention period, or extend retention if you need multi-year lookbacks.

Compute Modes

Behavioral metrics support two compute modes that control freshness and performance characteristics. In both modes, a Decisioning Gate or contact policy reads the metric the same way — a lookup against the metric’s precomputed value for that customer (and dimension key, if grouped). The modes differ only in how that precomputed value gets refreshed:

Realtime Mode

When computeMode is realtime, the metric’s value increments live: every time an impression or outcome event is recorded (via /api/v1/respond or bulk respond), the platform checks which realtime metrics match that event and updates their stored value in place — a non-blocking, fire-and-forget update that runs alongside the event write. There is no separate recompute step and nothing to schedule.
  • Freshness: Always current — reflects events recorded moments ago
  • Performance cost: A small write-path increment per matching event; no cost at decision/read time
  • Best for: Low-volume metrics, critical frequency caps, metrics that need sub-minute accuracy

Batch Mode

When computeMode is batch, the metric is recomputed on a schedule defined by batchIntervalMin, rather than incrementing on every event. The scheduled recompute is driven by the cron endpoint GET /api/v1/cron/recompute-metrics (CRON_SECRET-authed, like other platform crons; recommended cadence: every 5 minutes). Each tick, the cron scans every tenant’s active batch metrics and recomputes only the ones that are due — a metric recomputes only when its last-computed timestamp is older than its own batchIntervalMin, so a 60-minute metric doesn’t do work on every 5-minute tick. A manual “Compute Now” action is also available in the UI outside the schedule.
  • Freshness: Stale by up to batchIntervalMin minutes
  • Performance cost: Near-zero latency at decision time; recompute cost is paid on the cron tick, not per event
  • Best for: High-volume metrics, long windows, metrics where slight staleness is acceptable
Orphan cleanup on recompute: if you change a batch metric’s groupByDimensions (e.g. add or remove a grouping dimension), the next scheduled or manual recompute deletes any stale MetricValue rows still keyed on the old dimension shape. This prevents a Decisioning Gate from reading a stale per-dimension value left over from before the change. Realtime metrics don’t need this step — there’s no bulk recompute, so a dimension change just changes which key future increments write to.
The batchIntervalMin field is only used when computeMode is batch. Valid values are 5 to 1440 minutes (5 minutes to 24 hours).

How Metrics Feed into Rules

Behavioral metrics are referenced in two key places during the decision pipeline:

In Decisioning Gates (metric_condition)

A decisioning gate of type metric_condition evaluates a behavioral metric against a threshold to determine whether an offer qualifies for a customer. The metric-condition config specifies:
metric_condition is authored at the Eligibility or Fit Filters stage (hard filter only) — it does not participate in match-stage soft scoring. When the condition triggers (the operator comparison evaluates true), the offer is dropped from the candidate set. See Decisioning Gates for the full rule-type and stage model.

In Contact Policies

Contact policies reference metrics to enforce frequency caps and fatigue detection:
When the metric value exceeds the threshold, the contact policy suppresses the offer on that channel.

Limits

Each tenant can create a maximum of 20 behavioral metrics. This limit prevents excessive computation during decision time. Choose metrics that provide the most value for your decisioning rules.

Creating a Behavioral Metric

1

Navigate to Behavioral Metrics

Go to Studio > Behavioral Metrics in the sidebar.
2

Click Create Metric

Click the + New Metric button.
3

Name and describe

Enter a display name and description.
4

Select aggregation function

Choose from count, sum, avg, min, max, or ratio.
5

Select source field

Choose the outcome field to aggregate (impressions, positive, negative, neutral, converts, totalValue).
6

Set dimensions (optional)

Add up to 4 dimensions: offerId, channelId, creativeId, outcomeType, or any custom dimension registered for your tenant. Realtime metrics may only use the four fixed dimensions — pick batch mode for custom dimensions.
7

Set time window

Enter the rolling lookback window in days (1-3650, custom), or leave empty for all-time.
8

Choose compute mode

Select realtime for always-fresh values or batch for precomputed values with a refresh interval.
9

Add filter conditions (optional)

Define filter conditions to narrow which interaction records are included in the aggregation.
10

Save

Save the metric. It begins computing immediately from existing interaction history.

Recipes

Common behavioral metric patterns for real-world use cases:
Metric: Count of impressions per channel in 7 days
Usage: Create a contact policy that suppresses offers when channel_impression_7d > 3 for any channel.

API Reference

Create a Behavioral Metric

Request body:
Response (201 Created):

List Behavioral Metrics

Update a Behavioral Metric

Pass the metric id in the JSON body alongside the updated fields.

Delete a Behavioral Metric

Deleting a behavioral metric may break contact policies or decisioning gates that reference it. Review dependent rules before deleting.

Next Steps

Dimensions

Register the custom dimensions your behavioral metrics can group and filter by.

Algorithms & Models

Use behavioral metrics as features in scoring models.

Dashboards

Monitor metric values and trends in real time.