# Compass Analytics: Technical Reference

Source: https://dev.arcxp.com/content-recommendations/content-recommendations-admin-ui-analytics/

Technical detail behind Compass analytics: how clicks are attributed to exposures, the Advanced view and event-stream readiness score, how Catch Up numbers are sourced, and the analytics endpoints you can query directly.

This page covers the technical side of Compass analytics: how clicks are attributed, how to read the Advanced view when diagnosing your integration, and the endpoints behind the numbers.

For what the Overview dashboard shows and what each headline metric means, see [Compass Analytics Overview](https://dev.arcxp.com/en/products/compass/compass-analytics-overview.html)on the Arc Learning Center. For the editorial actions in the same app, see [Compass Admin UI: Editorial Controls](https://dev.arcxp.com/content-recommendations/content-recommendations-admin-ui-editorial-controls/).

> **Note:**
> 
> Compass analytics is **minute-grain, not real-time**. Every number is read from a projection that is rebuilt continuously. Expect a lag of minutes.

## How clicks are attributed

An **impression** is a recommendation _exposure_: a reader saw the story in a recommendation slot, qualified by the viewport. It is not a count of recommendation API requests or of lists rendered.

Clicks fall into three classes:

| Click class                        | What it is                                                                                                                 | How to use it                                                                                                                |
| ---------------------------------- | -------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------- |
| **Confirmed clicks**               | A click that carried valid attribution context _and_ matched an exposure of that same story inside the attribution window. | The **CTR numerator**. This is the customer-facing click number.                                                             |
| **Context-only attributed clicks** | A click that carried valid attribution context but never matched an exposure.                                              | Real evidence of recommendation-driven engagement. It does **not** raise CTR, and it must never be turned into a second CTR. |
| **Unattributed clicks**            | A click with no usable attribution context.                                                                                | Evidence that interaction happened — nothing more. **Never derive a rate from it** (see the caution below).                  |

> **Caution:**
> 
> Unattributed clicks are deduplicated only per source object, so a single click appearing in two source objects is counted twice. Treat the figure as “interaction occurred”, never as a precise count, and never as the numerator or denominator of a ratio.

### Why confirmed CTR is a lower bound

Matching a click to its exposure is **one-shot**. Event files arrive out of order — that is normal with batched delivery — and if a click is processed before the exposure that produced it, the click is _permanently_ classified as context-only. It is never promoted to confirmed when the exposure lands later.

So confirmed CTR slightly undercounts, and the size of the undercount moves with your delivery patterns. If you need to see how much is being missed, the context-only series in [Clicks by type](#clicks-by-type) is that gap.

## The advanced view

The **View all metrics** button allows you to dive deeper into performance of the recommendations API\*. This view is where you diagnose the quality of the data feeding recommendations, rather than the performance of the recommendations themselves.

### Clicks by type

The three click classes from above, charted:

- **Confirmed** — clicks matched to a specific recommended story.
- **Context only** — clicks with attribution context that couldn’t be matched to a served story.
- **Attributed (total)** — every click carrying attribution context: confirmed plus context-only.

A large context-only share relative to confirmed is the signature of exposure events arriving late or not at all, rather than of readers behaving differently.

### Event-stream readiness

_Strength of the behavioral data feeding the recommendations API._ A score from 0 to 100 with a state badge, the number of events in the window, the attribution rate, and the window itself (**Last 24 hours**).

The two score descriptions you’ll see are “_Strong enough to personalize. Recommendations are running on real behavioral signal._” at the healthy end, and “_Signal quality is low. Personalization may be unreliable._” at the weak end.

> **Caution:**
> 
> **The readiness score is a directional indicator, not a guarantee of recommendation performance.** It tells you whether your event stream looks healthy enough to personalize on; it does not predict CTR. A high score is not a promise of engagement, and a low score on a genuinely low-traffic website can be accurate rather than a defect.
> 
> Read it as a diagnostic on your integration, and act on the **dimensions** rather than the single number — they name what to change. The score carries a version identifier, so if you track it over time, check that alongside the value: a shift in the score without a matching shift in your event volume means the scoring changed, not your data.

Readiness describes your organization’s **whole recommender pipeline**, not one experience. It has no per-experience filter — Catch Up traffic is excluded from the attribution-completeness dimension but still counts toward total events and user reach.

### Readiness dimensions

Seven dimensions, each with a value and a status. The remediation text below is the guidance the UI itself shows:

| Dimension                    | What it measures                                                   | Unit             | If it’s below target                                                           |
| ---------------------------- | ------------------------------------------------------------------ | ---------------- | ------------------------------------------------------------------------------ |
| **Volume**                   | Total events received in the rolling window                        | events           | Increase event volume from your integration, or widen the time window.         |
| **Attribution completeness** | Share of recommendation events carrying usable attribution context | of events        | Include attribution context on recommendation events so clicks can be matched. |
| **Diversity**                | Number of distinct event types seen                                | event types      | Emit a wider range of event types, such as exposures, clicks, and conversions. |
| **Distinct users**           | Estimated distinct users seen                                      | users            | Drive more unique users to recommended content, or widen the window.           |
| **Engagement depth**         | Average events per distinct user                                   | events / user    | Encourage repeat interactions per user across a session.                       |
| **Item concentration**       | Share of activity captured by the single most active story         | in top item      | Spread recommendations across more items so activity is less concentrated.     |
| **Recency**                  | How recently the latest event arrived                              | since last event | Check that events are flowing in near real time without delivery lag.          |

Each dimension — and the overall score — carries one of five states:

| State                 | Meaning                          |
| --------------------- | -------------------------------- |
| **Healthy**           | Meeting the target.              |
| **Warn**              | Below target, still usable.      |
| **Weak**              | Well below target.               |
| **Insufficient data** | Not enough events yet to assess. |
| **Not applicable**    | Not measurable for this window.  |

**Attribution completeness** is the dimension to fix first if it’s low: it is what determines whether clicks can be matched to exposures, and therefore whether your CTR is meaningful at all. See [Recommendation Attribution](https://dev.arcxp.com/content-recommendations/sdk/attribution/) for the tagging that populates it.

Two readiness states are easy to mistake for a broken integration:

| What you see                                             | What it means                                                                                   | What to do                                                                                        |
| -------------------------------------------------------- | ----------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------- |
| A readiness score of 0 with **Insufficient data** states | **Not empty.** Events exist, but the rolling count is under the floor needed to assess quality. | Nothing is broken. Get more traffic through recommended content, or widen the window.             |
| No readiness reading at all                              | **Nothing has been projected yet** for this organization.                                       | Expected before the first events land. If events are flowing, treat it as an integration problem. |

### Readiness over time

The same score as a trend, which is the more useful read: a readiness score sliding down over several days points at an integration that is degrading, and is worth investigating even while the absolute number still looks acceptable.

### Item performance

_Top recommended items ranked by exposure volume._ A table of your stories covering the story, its title, impressions, confirmed clicks, CTR, and average served position.

Two things to know about this table:

- **Titles are best-effort.** The title is looked up at read time and shows as **(no title)** for a story that has since been deleted or is otherwise missing from the catalog. The metrics for that row are still valid.
- **It is a truncated ranking, not a paginated list.** There is no next page. If the view reports that results were truncated, the ranked list was longer than the row limit — narrow the time window or the surface filter to see further down.

A **Show raw data** affordance exposes the underlying response for the view.

## Where Catch Up numbers come from

Catch Up has no dedicated analytics endpoint. Its numbers come from the same recommendation analytics endpoints, filtered to the Catch Up experience — which is why the Overview labels match. Catch Up impressions are counted per material segment within a card, not per card, and only storyline-driven cards emit telemetry; see [Compass Analytics Overview](https://dev.arcxp.com/en/products/compass/compass-analytics-overview.html#reading-catch-up-analytics)for how that changes the numbers.

Per-story Catch Up impressions and confirmed clicks reach back only about 25 hours. If you need that history, capture the per-story figures on a daily schedule and retain them yourself.

The Catch Up section’s live indicator is derived from whether a projection exists and whether any cards were served in the window, not reported directly. “No activity in window” and “nothing projected yet” are different states with different causes.

## The endpoints behind the numbers

Everything in the Admin UI is served by four read-only endpoints, which you can also query directly:

| Endpoint                     | Powers                                                                          |
| ---------------------------- | ------------------------------------------------------------------------------- |
| GET /analytics/v1/ctr        | Impressions, clicks, CTR, average served position, and the clicks-by-type chart |
| GET /analytics/v1/engagement | Card-level Catch Up counters                                                    |
| GET /analytics/v1/items      | Item performance and the pinned story’s own metrics                             |
| GET /analytics/v1/readiness  | The readiness score, dimensions, and trend                                      |

The three time-series endpoints share a query shape: a required `window_start`(inclusive) and `window_end` (exclusive) in UTC, an optional `granularity` of `minute`, `hour`, or `day` (`hour` by default), and optional `site_id` and `surface_id` filters. `/items` takes a `limit` instead of a `granularity`. `/readiness` takes only an optional window and defaults to the last 24 hours.

> **Caution:**
> 
> The `experience` parameter — `recommendations`, `catch_up`, or `all` — **defaults to `recommendations`**. Omit it when you meant to query Catch Up and you will get recommendation numbers back with no error and no warning. `/readiness` has no `experience` filter by design; a value passed there is ignored.

Two limits to design around: a response is capped at 1,500 time buckets and a query window at 92 days, and exceeding either is an error rather than a truncated result. Requested windows are snapped to bucket boundaries, and the response echoes the window it actually used — read that back rather than assuming your requested window was honored exactly.

An empty window is a successful response with zero counts and a null CTR; a failed request is an error. Don’t record an error as a period with no activity. No freshness information means no underlying data, not fresh data.
