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
ThesourceField 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)
channelIddimension: “Clicks per channel in 7 days” (one number per channel per customer)channelId+offerIddimensions: “Clicks per channel per offer in 7 days”channelId+ a customcampaigndimension: “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 ontointeraction_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 theinteraction_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 theinteraction_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-metricscron 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, andoutcomedimensions can be backfilled onto existinginteraction_historyrows, 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_attributedimensions 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 pipelineoutcomesnode — but not on plain/api/v1/respond, which doesn’t load the customer profile on that path.journeydimensions are also forward-only, and currently capture no value anywhere: the journey engine doesn’t write tointeraction_historyyet, so ajourney-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/journeydimension 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 withcomputeMode: "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
ThewindowDays 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
nullor omit for an all-time aggregation - The window is always rolling — it looks back from the current moment
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
WhencomputeMode 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
WhencomputeMode 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
batchIntervalMinminutes - 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 typemetric_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:Limits
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:- Frequency Cap
- Conversion Cooldown
- Channel Fatigue
- Response Rate Gate
- High-Value Targeting
- Cross-Sell Detection
Metric: Count of impressions per channel in 7 daysUsage: Create a contact policy that suppresses offers when
channel_impression_7d > 3 for any channel.API Reference
Create a Behavioral Metric
List Behavioral Metrics
Update a Behavioral Metric
id in the JSON body alongside the updated fields.
Delete a Behavioral Metric
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.