Skip to main content
Targeting is publisher-controlled. On each fragment you define what you accept: the targeting parameters you allow as ctx.*, plus a match policy. A card serves only where what you accept overlaps the page. Serving is that intersection; the policy is your lever.

Two sides meet at serving

Think of it as two sides that have to agree. On the supply side, your fragment declares what it accepts, including its match policy (match_mode). On the demand side, each card carries its own targeting (its advertiser_id, the page topics it is for). A card is eligible only when you accept it on the fragment and it clears that policy. The page context, meaning a brand detected on the page and your ctx.* values, then helps pick which eligible card serves.
Two-sided targeting: publisher supply intersected with creative demand and page context produces serving

The hard gate is what the publisher accepts (and the match policy): only brand_only returns no-fill. Page context and ctx.* are soft signals that pick which eligible creative serves.

This is an allow-list you own, not an automatic match. A card does not serve just because a page mentions its brand. It serves because you accept it on that fragment and it clears your policy. The hard gate is the policy: only match_mode=brand_only returns no-fill () when no eligible card matches a detected brand. Page context and ctx.* are soft preferences layered on top: they shape which eligible card is chosen, but on their own they never force a no-fill.

1. What you accept: key-values → ctx.* params

The key-values you define on a fragment are the parameters you accept on that fragment. Each becomes a ctx.<key>=<value> query parameter your edge appends to the fragment URL, passing page context to doubleshift to help it pick the most relevant card.
Key-values → ctx.*
You can append them statically (baked into the URL for that fragment) or per page from your edge, e.g. setting ctx.section from the page’s section. On the endpoint, each non-empty ctx.* value is matched against the candidate cards (their brand and keywords) to prefer the most relevant one. It is a soft preference, not a filter: a ctx.* value that no card matches is simply ignored, and it never forces a no-fill on its own. Because they live in the URL, the response stays cacheable by URL.

2. Your policy: match_mode (per fragment)

Each fragment carries a card matching policy, match_mode, your lever for how tightly serving is tied to a brand actually detected on the page: The policy tightens the supply side from the most permissive (open) to the most selective (brand only). It never overrides eligibility: a card still has to be one you accept and one the page context matches.

How a brand match is resolved

When match_mode is not open, doubleshift matches the page against your eligible cards by advertiser_id: a card matches a page when its advertiser_id equals a brand on that page. advertiser_id is the join key between a brand and a creative. The policy only controls the fallback: brand first falls back to any eligible card; brand only returns no-fill instead.
Roadmap: accepting external creativesToday the creatives are your own cards (or platform-wide cards), so the demand side is yours. In the future, a publisher will be able to accept external creatives supplied by separate advertiser accounts onto a fragment, and the same controls on this page (what you accept and your match_mode policy) decide what you let onto your inventory. The model does not change; only the source of the demand widens.
Your edge does not changeTightening match_mode or editing what a fragment accepts changes nothing in your edge snippets. Your edge keeps sending page_url (and any ctx.*) exactly as before. doubleshift resolves the brand match and applies your acceptance rules server-side from the page_url you already pass.