> ## Documentation Index
> Fetch the complete documentation index at: https://docs.shftd2.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Targeting

> Targeting is publisher-controlled. On each fragment you define what you accept: the ctx.* params and a match policy. Serving is the intersection.

export const Pill = ({variant = "alpha", children}) => <span className={`ds-pill ${variant}`}>{children}</span>;

Targeting is **publisher-controlled**. On each [fragment](/fragments) 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.

<Frame caption="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.">
  <img src="https://mintcdn.com/doubleshift/snaBZNZYPEBgZPe2/images/diagrams/targeting.svg?fit=max&auto=format&n=snaBZNZYPEBgZPe2&q=85&s=56b11a81aa937c3385b16259479e1885" alt="Two-sided targeting: publisher supply intersected with creative demand and page context produces serving" width="720" height="470" data-path="images/diagrams/targeting.svg" />
</Frame>

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 (<Pill variant="code-204">204</Pill>)
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](/fragments) 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.

```text title="Key-values → ctx.*" theme={"theme":"css-variables"}
# Fragment key-values (console) — what this fragment accepts:
section: auto, moto
sponsor: renault

# Become ctx params on the endpoint (the page context):
&ctx.section=auto&ctx.sponsor=renault
```

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:

| Policy          | What you allow                                                                                                                     |
| --------------- | ---------------------------------------------------------------------------------------------------------------------------------- |
| **open**        | Accept any eligible card. A brand on the page is not required. (Default.)                                                          |
| **brand first** | Prefer a card whose brand is detected on the page; if no detected brand has an eligible card, fall back to any eligible card.      |
| **brand only**  | Accept only a brand-matched card; if no detected brand has an eligible card, answer no-fill (<Pill variant="code-204">204</Pill>). |

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.

<Note>
  **Roadmap: accepting external creatives**

  Today 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.
</Note>

<Tip>
  **Your edge does not change**

  Tightening `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.
</Tip>
