Skip to content

Deploying and Rendering Recommendations

Audience: Front-end developers and product owners integrating Arc XP recommendations into a website, app, or email program.

Stack used in examples: TypeScript + React.

This guide comes in two parts. Part 1 — Rendering recommendations — is the reusable foundation: how a single recommend call returns display-ready cards, and how to render one card (and one graceful component) well. Part 2 — Recommendation surfaces — is the catalog of where to deploy recommendations, what each placement is good at, and what it takes to ship each one. Every surface in Part 2 renders its cards the way Part 1 describes, so build the rendering component once and reuse it everywhere.

Rendering recommendations

The Recommendations API returns a ranked list of recommendations, each carrying a card object with display fields sourced from the underlying content document. There is no separate inflation call: one round trip gives you everything needed to render story cards.

  1. Recommend — call GET /recommend/v1/recommendations to get a ranked list of items, each with a card.
  2. Render — map each card onto a card component.

This part walks through each step, gives you a runnable TypeScript + React snippet, and shows how to handle empty results and error states gracefully.

Prerequisites

  • A site you can issue requests for (you’ll need its site_id).
  • A way to identify the current viewer (you’ll pass that as user_id — anonymous IDs are fine, as long as they’re stable per visitor). See User ID Guidance.
  • An HTTP client. The examples use fetch; any client is fine.

Step 1: Fetch recommendations

Endpoint

GET https://{org}-config-prod.api.arc-cdn.net/recommend/v1/recommendations

Query parameters

NameRequiredDescription
site_idyesThe site you’re requesting recommendations for.
user_idyesStable identifier for the current viewer (logged-in or anonymous).
num_resultsnoHow many recommendations to return. 1–50, default 5.
sectionnoNarrow the candidate pool to a section before ranking.
content_typenoNarrow the candidate pool to a content type (e.g. video) before ranking.
subscription_tiernoCaller subscription level (e.g. "free", "premium").
device_typenoDevice class (e.g. "mobile", "desktop").

The section and content_type filters are how each surface in Part 2 keeps sectional and format-specific placements on-topic. For the full reference — matching rules and additional examples — see Compass API filters.

Response shape

{
"recommendations": [
{
"item_id": "ABC123…",
"score": 0.87,
"card": {
"title": "Election results upend mayoral race",
"date": "2026-06-01T12:00:00Z",
"author": "Alice Reporter",
"type": "article",
"is_premium": false,
"categories": ["politics"],
"tags": ["election", "2026"],
"url": "https://example.com/story",
"thumbnail_url": "https://cdn.example.com/story.jpg"
}
},
{ "item_id": "DEF456…", "score": 0.81, "card": null }
],
"attribution": { "exposure_id": "exp-…", "issued_at": "…" }
}

score is the post-reranking relevance score (0.0–1.0). Editorial signals — boosts, buries, and pins — have already been applied. You can use score to drive UI affordances (badging the top result, sorting, etc.), but the array order is already the recommended display order.

card is the display payload. Every field on the card is optional, and card itself is null when no matching content document was found (for example, an editor-pinned item whose document has not yet been synced). Cards should degrade gracefully when individual fields are missing — the only safe assumption is that a recommendation always has item_id and score.

card.thumbnail_url is a ready-to-use, feed-card-sized image that is safe to render directly. See Card images for its size, freshness, and fallback behavior.

TypeScript example

type ItemCard = {
title: string | null;
date: string | null;
author: string | null;
type: "article" | "video" | "podcast" | null;
is_premium: boolean | null;
categories: string[];
tags: string[];
url: string | null;
thumbnail_url: string | null;
};
type ScoredItem = {
item_id: string;
score: number;
card: ItemCard | null;
};
type RecommendationResponse = {
recommendations: ScoredItem[];
};
async function fetchRecommendations(params: {
org: string;
siteId: string;
userId: string;
numResults?: number;
}): Promise<ScoredItem[]> {
const url = new URL(`https://${params.org}-config-prod.api.arc-cdn.net/recommend/v1/recommendations`);
url.searchParams.set("site_id", params.siteId);
url.searchParams.set("user_id", params.userId);
if (params.numResults) {
url.searchParams.set("num_results", String(params.numResults));
}
const response = await fetch(url, {
headers: { Accept: "application/json" },
});
if (!response.ok) {
throw new Error(`Recommendations request failed: ${response.status}`);
}
const body = (await response.json()) as RecommendationResponse;
return body.recommendations;
}

Step 2: Map to card fields

A usable story card needs four fields at minimum:

Card fieldSource fieldNotes
Headlinecard.titleDisplay title for the card.
Imagecard.thumbnail_urlFeed-card-sized image, already resized (JPEG, within 640×640), with a fallback to the source image. See Card images.
URLcard.urlThe link the card navigates to on click.
Bylinecard.authorAuthor display string. Fall back to publication name if absent.

Anything beyond those four (kicker, card.date, section label, premium badge from card.is_premium) is layered on top of this minimum set. Build cards that degrade gracefully when an optional field is missing, and skip rendering when card is null or the required fields are missing.

type StoryCardProps = {
item: ScoredItem;
};
function StoryCard({ item }: StoryCardProps) {
const card = item.card;
if (!card || !card.url || !card.title) {
return null;
}
return (
<a href={card.url} className="story-card">
{card.thumbnail_url ? <img src={card.thumbnail_url} alt="" /> : null}
<h3>{card.title}</h3>
{card.author ? <p className="byline">{card.author}</p> : null}
</a>
);
}

Card images

When you ingest a content item, you supply a source image as thumbnail_url on the content payload (see the content endpoint). Arc XP generates a single feed-card-sized derivative from that source image once, at ingest time. There is one derivative per content item, shared across every site and recommendation surface that renders the item. The derivative is what you receive as card.thumbnail_url on recommendation responses and on the Catch-Up Card — no separate image call or hydration step is needed.

  • Format: JPEG.
  • Size: fitted within 640×640 pixels, preserving the source image’s aspect ratio. The image is scaled down to fit the box; it is not cropped or padded to a fixed square. Because it arrives already resized, you generally do not need to resize it again before rendering it into a card.
  • Freshness: the derivative is regenerated only when the item’s source image URL changes. Re-publishing an item with the same thumbnail_url reuses the existing derivative, so change the source image URL when you need a new image to take effect.
  • Fallback: when a derivative hasn’t been generated yet, or generation didn’t complete, card.thumbnail_url falls back to the original source image URL you supplied. Image generation never blocks or delays ingestion, so a newly published item is immediately renderable with its source image while its derivative is prepared.

card is null (and therefore has no image) only when no matching content document is found — for example, an editor-pinned item that hasn’t synced yet.

Handling empty results and errors

There are three states to design for. Don’t render a half-broken card grid in any of them.

StateWhen it happensRecommended UI
EmptyThe API returns recommendations: []. Common for new sites, new users, cold-start scenarios.Show a fallback rail (editorial picks, most-read) — never an empty grid.
Network errorThe recommendations request fails (timeout, 5xx, network).Show the same fallback rail. Log the error for observability.
ValidationThe API returns 422. Almost always a caller bug (missing/invalid params).Surface during development. In production, treat as the network-error case.
function RecommendationsRail({ org, siteId, userId }: { org: string; siteId: string; userId: string }) {
const [items, setItems] = useState<ScoredItem[] | null>(null);
const [error, setError] = useState<Error | null>(null);
useEffect(() => {
let cancelled = false;
(async () => {
try {
const scored = await fetchRecommendations({ org, siteId, userId, numResults: 8 });
if (!cancelled) setItems(scored);
} catch (e) {
if (!cancelled) setError(e as Error);
}
})();
return () => {
cancelled = true;
};
}, [org, siteId, userId]);
if (error) return <EditorialFallbackRail />;
if (items === null) return <RailSkeleton />;
if (items.length === 0) return <EditorialFallbackRail />;
return (
<div className="recommendations-rail">
{items.map((item) => (
<StoryCard key={item.item_id} item={item} />
))}
</div>
);
}

Checklist before shipping

  • You’re passing a stable user_id per visitor (not a per-page-load random value) — see User ID Guidance.
  • You’re requesting an appropriate num_results for the surface (don’t fetch 50 to render 4).
  • You have a fallback rail for empty results.
  • You have a fallback rail for network/validation errors.
  • Cards render with only the four required fields, even when optional fields are missing.
  • Recommendations with card === null or missing required fields are skipped (or routed to a placeholder).

Recommendation surfaces

Part 1 covers how to render one card and one graceful component. This part covers where to deploy recommendations: the surfaces across your site, app, and email program where personalized recommendations earn their keep, and what it takes — on both the product and engineering side — to stand each one up well.

Every surface here calls the same recommend endpoint and renders each returned card exactly as Part 1 describes. What changes between surfaces is the presentation, the signals you feed back to the model, and — for placements that need to stay in a section or a content format — the section and content_type filters you add to narrow the candidate pool. Each surface below spells out its surface-specific parameters and UX.

For endpoints, authentication, and data ingestion in depth, this section’s companion is the Compass API Developer Guide.

Principles that apply to every surface

A few design and engineering decisions show up in every placement. Get these right once and they pay off across all surfaces:

  • Send a click event back when a reader engages. Every recommendation a reader clicks is a signal that trains the next one — closing this loop the moment a reader taps a recommended card is the single highest-leverage thing you can do to improve relevance over time. How you close it depends on how you integrate: if you’re calling the APIs directly, post a click event to your Customer Data Platform (CDP) and forward it to the Collector’s /collector/v1/events endpoint. If you’re using the Compass Web SDK, you don’t post events by hand — tag each rendered recommendation with the data-arc-compass-* attributes (or register it imperatively) and the SDK emits correctly-timed recommendation_exposure and recommendation_click events carrying the Recommender’s attribution identifiers for you. See Recommendation Attribution for the tagging flow.
  • Label personalized content, don’t hide it. “For You,” “Picked for you,” “Based on what you’ve read” — explicit labeling builds trust and sets the right expectation. It also gives readers a reason to engage with a module that could otherwise read as generic recirculation.
  • Use stable, anonymized user IDs. The model learns per-user, so the same reader must resolve to the same anonymized user_id across sessions, devices, and surfaces — never raw PII. See User ID Guidance for the full rules.
  • Exclude the current article from contextual surfaces. On an article page, filter the current article’s item_id out of the returned list on render. You don’t want a “read next” module recommending the article the reader is already on.

Surface 01  ·  Homepage “For You” module

A personalized module on your front page, the flagship surface.

  • UX goal: Give returning readers a reason to scroll past the top of the homepage by surfacing content they’re likely to care about.
  • Best for: Publishers with a meaningful share of returning, identified, or cookied traffic.
  • API mode: General personalized recommendations.

The experience

The “For You” module lives on the homepage, typically below the main news hierarchy — after the hero and the top editorial rails, but above the deep tail of section blocks. This placement matters: putting it above editorially curated news signals that personalization replaces the newsroom’s judgment, which isn’t what most publishers want. Putting it after the lead coverage positions it as “here’s what else is worth your time,” which is exactly the job it does well.

A typical layout is a labeled rail — “For You,” “Picked for you,” or “Your briefing” — with 6 to 12 cards in a horizontal scroll on mobile and a grid on desktop.

For anonymous or first-time readers, the module still renders — the API returns popularity-weighted results by default for cold-start users — but you can choose to label it differently (“Most popular today”) until the reader has accumulated enough signal. That’s a product call, not an API one.

How to implement it

On page load, call the recommend endpoint with the reader’s user_id; no filter is needed. Request 10–15 items (num_results), more than you plan to show, and render each card as shown in Rendering recommendations.

  • Request more items than you plan to show. That gives you headroom to filter out articles the reader has recently viewed or items whose card is null.
  • Cache carefully. Personalization is per-user, so typical CDN page caching won’t apply to this module. Most implementations render the module client-side after the shell loads, or fetch it at the edge keyed on user_id.
  • Send the click event back on tap (per the principle above) — the homepage is your highest-volume click source, so don’t skip it.

Things to watch for

Because the homepage is your highest-traffic surface, this module’s performance matters. Fetch it asynchronously so it never blocks above-the-fold render, and have a sensible fallback — popular articles from your CMS, say — ready if the recommend call fails or times out (see the empty/error handling in Part 1).

Test with anonymous and logged-out sessions explicitly. A “For You” module that looks identical to every reader on their first visit will feel broken even when it’s working as designed.


Surface 02  ·  “For You” tab or landing page

A full-surface, personalized destination — the long-form version of the homepage rail.

  • UX goal: Give highly engaged readers a dedicated home for personalized content, and give the app a prominent hook for habitual use.
  • Best for: News apps, logged-in experiences, and publishers with a clear power-user segment.
  • API mode: General personalized recommendations, typically paginated or infinitely scrolled.

The experience

Where the homepage rail is an appetizer, the “For You” tab is the meal. In a mobile app, it usually sits as a first-class tab alongside Home, Sections, and Saved. On desktop web, the equivalent is a dedicated /for-you landing page reachable from the main nav or a persistent CTA for logged-in users.

The experience is a deep, scrollable feed — cards or list items, one recommendation per row or in a staggered grid, loading more as the reader scrolls.

How to implement it

Call the same unfiltered recommend endpoint, but pull more results (num_results) and paginate: fetch the first page on tab load, then fetch additional pages as the reader scrolls. Render each card as shown in Rendering recommendations.

  • Treat this as the highest-signal surface you own. A reader who sits in the For You tab and scrolls is generating dense behavioral data. Make sure you’re capturing click, deepest_scroll, and engaged_read events here — they improve recommendations across every other surface.
  • De-duplicate against session history. On mobile especially, readers may see the same items across the homepage rail and the For You tab. Keep a client-side set of item_ids the reader has already been shown this session and filter them out on render.
  • Consider a refresh affordance. A pull-to-refresh on mobile or a “Refresh feed” button on web, which re-calls the API, gives readers an explicit way to get a new set of picks.
  • Log impressions, not just clicks, if your analytics can support it. Knowing which recommendations were shown but skipped is valuable product signal, even if you don’t send it back to the Collector.

Things to watch for

A full “For You” surface sets a high bar for relevance. If the model is still cold — newly provisioned, or the customer hasn’t backfilled historical events — this surface will expose that thinness more than a homepage rail will.

If you already have an editorially curated feed or a “Saved” tab, think carefully about tab order and labeling. “For You” should feel like a distinct, additive destination — not a replacement for what readers already reach for.


Surface 03  ·  Inline article recommendation

A single, contextually relevant recommendation dropped mid-article, where engaged readers meet it.

  • UX goal: Catch readers at peak engagement and offer the one most relevant next story — without derailing the one they’re in.
  • Best for: Long-form articles, feature stories, and explainer content where readers are likely to read deeply.
  • API mode: General personalized recommendations, narrowed to the current article’s section with the section filter.

The experience

Unlike the homepage rail or the For You tab, this is a single-item placement embedded in the body of the story itself — typically between the fifth and seventh paragraph, where reader engagement is highest but attention hasn’t yet begun to drop off. Readers who make it that far into an article are highly qualified; a well-placed, contextually relevant recommendation here converts at rates that end-of-article modules often can’t match.

The UX is intentionally restrained: a single card or a compact two-line teaser, visually distinct from the article body (a subtle tint, a thin top-and-bottom rule, an “Also in [section]” or “Related reading” label), but not so loud that it interrupts the reader’s flow. Think of it less as an ad unit and more as a well-edited sidebar — the kind of thing an editor might add to a print feature.

Because the recommendation is narrowed to the current article’s section, the result stays topical. A reader halfway through a piece in your housing section gets a contextual next read from that same section, not an off-topic recommendation from their general reading history.

How to implement it

Call the recommend endpoint with the reader’s user_id and the current article’s section as the section filter:

GET /recommend/v1/recommendations?site_id=my-site&user_id=user-abc&section=housing

Render the resulting card as shown in Rendering recommendations.

  • Fetch once, render once. Only one item goes in-body. Request five results (num_results) and take the top one that (a) isn’t the current article and (b) has a non-null card.
  • Render server-side where possible. Because the article’s section is known at page render time, this is one of the few personalized surfaces you can render into the initial HTML. That’s good for both performance and SEO.
  • Fire the click event on tap (per the principle above). Inline placements tend to have strong click signal — don’t leave it on the table.
  • Consider an engaged_read event on the article itself, triggered when a reader has been on page past a dwell threshold or hit a deep scroll mark. These richer events meaningfully improve model quality, and articles are the natural place to capture them.

Things to watch for

Placement is the whole game. Too early and it feels like an interruption; too late and you’ve missed the window. Five to seven paragraphs in is the general heuristic, but test with your own content — feature-length pieces may warrant a later insertion.

Be cautious about stacking this with display ads in the same vertical span. If there’s an ad slot at paragraph four and a recommendation at paragraph six, readers experience both as interruptions and tune both out.


Surface 04  ·  Article end cap (story ender)

The recirculation module at the bottom of the article — the workhorse of content recirculation.

  • UX goal: Capture readers who finished the article and give them an obvious next click before they bounce.
  • Best for: Every article page. This is the default recirculation pattern on almost every news site for a reason.
  • API mode: General personalized recommendations, narrowed to the article’s section with the section filter.

The experience

The story ender is the module that sits immediately after the article body, typically labeled “Read next,” “You might also like,” or “More from [section].” It’s the most common recirculation surface on the web, and with personalized recommendations powering it, it becomes substantially more effective than a static “latest from this section” list.

A typical layout shows four to six cards in a grid, each with thumbnail, headline, section, and optionally a timestamp. On mobile, this often becomes a vertical stack or a horizontal swipe. The visual treatment should be clearly distinct from the article body — a different background tone, a section heading, a rule above — so readers understand they’ve moved from the article into recirculation.

Unlike the inline recommendation, which is disciplined to a single item, the end cap is where you can be a little more generous. A reader who made it to the bottom has finished the article; offering multiple options respects that they may want something similar, something from the same author, or a palate-cleansing change of topic.

How to implement it

Same section-filtered call as the inline recommendation, but request more items (num_results):

GET /recommend/v1/recommendations?site_id=my-site&user_id=user-abc&section=housing

Render each card as shown in Rendering recommendations.

  • Mix personalized with editorial if you want. Some publishers render the top three API results followed by an editor’s-pick slot or a “Most read” item. That hybrid approach keeps the module feeling curated while still getting the personalization lift.
  • Exclude the current article from the rendered list. You don’t want to show a reader the article they just finished.
  • The section filter keeps the end cap inside the same editorial neighborhood the reader is already in, which is a natural fit for a “More from [section]” label. See Surface 06 for more on the filter-driven pattern.
  • Track clicks and log impressions. The end cap is a high-volume surface; the event data it generates is some of the most valuable training signal you’ll have.

Things to watch for

If you already have an end-cap module powered by a static rule (“latest from this section,” “more from this author”), consider A/B testing the personalized version against it rather than cutting over cold. It’s a clean comparison and a natural way to build internal confidence in the recommendations quality.

Watch the overlap between the inline recommendation and the end cap. If both surfaces are live on the same article and both use the same section filter, they’ll pull from the same result set. Request more results than you need in the end cap and filter out whatever the inline recommendation has already shown.


Surface 05  ·  “For You” email

Personalized recommendations delivered directly to a reader’s inbox — on their schedule, not yours.

  • UX goal: Drive reengagement and return visits by delivering a curated, personalized digest without requiring the reader to open the app or site first.
  • Best for: Publishers with an established newsletter program and an identified, emailable audience.
  • API mode: General personalized recommendations (all-personalized variant), optionally narrowed to the lead story’s section with the section filter (editorial-plus-personalized variant).

The experience

There are two strong patterns for using recommendations in email, and many publishers will want to run both.

The first is the fully personalized “For You” digest — an email that consists entirely of recommended articles for each subscriber, typically six to ten items, sent on a regular cadence (daily or several times a week). It’s a powerful reengagement tool: the content is different for every subscriber, and the cadence keeps your publication in the reader’s inbox without requiring editorial production effort beyond the template itself.

The second pattern is a hybrid — what’s often called a “1+3” or “1+N” email. An editor authors a single anchor piece (a top story, a “pick of the day,” a staff column), and the rest of the email is filled with personalized recommendations, often narrowed to that lead story’s section. The editorial voice does the work of signaling why this email is worth opening; the personalization does the work of making the rest of the email relevant to each individual reader. For publishers who already send editor’s-pick emails, this pattern is a natural evolution — it keeps the editorial lead and dramatically expands what the tail of the email can do.

Both patterns live or die on the quality of the subject line and the top item. Neither is a substitute for editorial judgment on the lead — they’re a way to extend its reach.

How to implement it

At send time, iterate over your subscriber list and call the recommend endpoint once per subscriber. For the fully personalized variant, call without a filter. For the 1+N variant, narrow to the section of the editor’s selected item with the section filter:

GET /recommend/v1/recommendations?site_id=my-site&user_id=user-abc[&section=editor-pick-section]

Render each card as shown in Rendering recommendations.

  • Include UTM parameters on every link, and make sure clicks back to your site still resolve to a user_id your front-end can recognize. You want the click events from email to feed back into the same model that powers your on-site surfaces.
  • Request more items than you’ll render. Email templates are visually rigid; if one of your six slots fails to hydrate, you want a seventh ready to take its place rather than sending a broken layout.
  • Send a click event on link click, ideally via a redirect endpoint that logs the event server-side to your CDP (and forwards to the Collector) before forwarding the reader to the article. This keeps email-driven engagement in the same training signal as on-site engagement.
  • For the 1+N pattern, pass the section of the editor’s lead item through the section filter so the personalized tail is topically related, not a grab bag.

Things to watch for

Email deliverability and list hygiene matter as much as personalization quality. A perfectly personalized email that lands in spam is a wasted call. Make sure your email infrastructure is solid before investing heavily in the content selection layer.

Be thoughtful about cadence. A fully personalized daily digest can feel algorithmic in a way some readers bristle at; framing it as “your briefing” or pairing it with a clear sender identity (“From the newsroom”) softens that. Watch unsubscribe rates on the fully personalized variant especially.

If you run both patterns, hold them on different sending schedules so subscribers don’t receive a personalized digest and a 1+N email on the same day from the same publisher.


Surface 06  ·  Section rails and content-type placements

Personalization scoped to a section page, a video rail, or a podcast carousel — the same recommendations engine, narrowed to the slice of catalog the placement is contractually about.

  • UX goal: Make sectional and format-specific surfaces personalized without leaking off-topic items into them.
  • Best for: Section landing pages (Politics, Sports, Business), homepage video rails, podcast carousels, or any placement where the slot itself is defined by a category or a content type.
  • API mode: General personalized recommendations, narrowed by the section and/or content_type filter parameters before ranking.

The experience

A reader on /sports who scrolls past the editorial lead sees a “Recommended for you in Sports” rail — items the model thinks they’ll like, but only items tagged into Sports. A homepage rail labeled “Watch next” returns only video items, even when the reader’s history is overwhelmingly text. The personalization model is the same as everywhere else; the filter simply narrows the candidate pool before ranking, as covered in the filters reference linked below.

This pattern matters because it lets you run personalized rails on placements that would otherwise be forced to fall back to editorially curated lists.

How to implement it

Add section, content_type, or both to the standard recommend call:

GET /recommend/v1/recommendations?site_id=my-site&user_id=user-abc&section=politics&content_type=video

Render each card as shown in Rendering recommendations.

  • Combine filters with the same discipline as every other surface: render each item’s card, exclude items the reader has already seen this session, and send a click event back on tap.

Things to watch for

A narrowly scoped section or a thinly populated content type can surface less personalization signal than a broad one. If a niche-section rail looks generic, that usually means not enough behavioral data has accumulated inside that slice yet — not that filtering itself is broken. Cold-start fallback to popular items still applies inside a filtered request.

If a placement is contractually about a section or format, prefer filters over post-hoc client-side exclusion. Letting the model rank the right candidates from the start produces a tighter, gap-free response.

For the full reference and additional examples, see Compass API filters.


Choosing where to start

Six surfaces is a lot to ship at once, and you shouldn’t. Most customers see the strongest return on a sequenced rollout that builds signal and confidence before expanding reach:

  • Start with the article end cap. It’s the highest-volume, lowest-risk surface. Every article page gets one, readers are already expecting a recirculation module there, and it generates dense click data that trains the model for every other surface.
  • Add the homepage For You module next. Higher visibility, but the end cap has already been collecting signal by the time you ship it, so the experience on day one is meaningfully better than launching the homepage cold.
  • Layer in inline article recommendations once you’re confident in the model’s relevance on longer-form content. The UX bar is higher here — a bad inline recommendation is more jarring than a bad end cap.
  • Ship the For You email program alongside or after the homepage. Email drives return visits that further enrich the training signal, so it compounds well with on-site surfaces.
  • Build the dedicated For You tab or landing page last. It has the highest quality bar because readers have explicitly opted into personalized content. Wait until the model has enough signal that a full-screen experience stands on its own.
  • Layer in section rails and content-type placements opportunistically. Filter-scoped surfaces (Surface 06) aren’t a sequenced step so much as a tool you reach for whenever a placement is contractually about a section or a format. They’re often the right answer for refreshing existing editorially curated rails on section pages once the broader rollout is healthy.

The feedback loop is the product

A pattern that’s worth underlining: every surface in this guide gets better because of every other surface. A click on the homepage rail improves the inline recommendation three articles later. An engaged_read event on a feature story sharpens the email digest tomorrow morning. The “For You” tab, if you ship it, becomes the densest source of training data you have.

That only works if you’re consistent about sending events back. A deployment that reads from the Recommendations API without ever posting events to the Collector API is a deployment whose quality never improves. Treat the click event as a mandatory half of every recommendation render, not an optional enhancement.