Skip to main content

Context Match and Identity Match

TMP defines two operations. They are independent by design — processed in separate infrastructure, carrying different data, answering different questions. The publisher joins their results to make the final activation decision. This page walks through both operations and the join using a concrete example.

The Scenario

A retail publisher has three active AdCP media buys from a buyer agent. Each media buy contains packages:
  • Package A: Sponsored products — coffee brands, carousel format, available on search and category pages
  • Package B: Homepage takeover — premium display, available on the homepage only
  • Package C: Seasonal promotion — iced beverages, native format, available site-wide during summer
A shopper searches for “best cold brew.” The publisher’s search results page loads.

Context Match

The publisher sends a Context Match request to the TMP Router, which fans out to each buyer’s agent. The request contains the page context. It contains no user identity and no package list — the buyer agent uses its synced package set for this placement.

What the publisher sends

{
  "type": "context_match_request",
  "request_id": "ctx-7f3a",
  "property_rid": "01916f3a-7b2c-7000-8000-000000000001",
  "property_id": "retailer-web",
  "property_type": "website",
  "placement_id": "search-results-grid",
  "artifact_refs": [
    { "type": "custom", "value": "search:beverages-coffee" }
  ],
  "context_signals": {
    "topics": ["632"],
    "keywords": ["cold brew", "iced coffee", "coffee beans"],
    "sentiment": "positive",
    "summary": "Shopper searching for cold brew coffee products"
  },
  "geo": { "country": "US", "region": "US-CA" }
}
The buyer agent sees what the page is about — coffee, positive purchase intent, California — and evaluates its packages against that context. Note what is absent: no user ID, no device information, no session token, no IP address.

What the buyer responds

The buyer agent evaluates each package against the context. Package B is homepage-only, so it does not match a search results page. Packages A and C match. The buyer returns an offer for each.
{
  "type": "context_match_response",
  "request_id": "ctx-7f3a",
  "offers": [
    {
      "package_id": "pkg-A",
      "brand": { "domain": "bluebottle.example.com", "brand_id": "blue_bottle" },
      "summary": "Cold brew coffee carousel — featuring top-rated blends",
      "creative_manifest": {
        "format_id": { "agent_url": "https://retailer.example.com", "id": "sponsored_carousel" },
        "assets": {
          "items": {
            "type": "catalog-asset",
            "items": [
              { "gtin": "gtin-001", "image_url": "https://cdn.example.com/gtin-001.jpg" },
              { "gtin": "gtin-002", "image_url": "https://cdn.example.com/gtin-002.jpg" }
            ]
          }
        }
      },
      "macros": {
        "sponsor_label": "Sponsored by Blue Bottle"
      }
    },
    {
      "package_id": "pkg-C",
      "summary": "Free shipping on iced beverages — summer promotion",
      "price": { "amount": 0, "currency": "USD", "model": "flat" },
      "creative_manifest": {
        "format_id": { "agent_url": "https://retailer.example.com", "id": "native_text" },
        "assets": {
          "headline": { "content": "Free shipping on iced beverages" },
          "body": { "content": "Summer promotion — order any iced beverage and get free delivery." },
          "cta": { "content": "https://shop.example.com/promo/summer-iced" }
        }
      }
    }
  ],
  "signals": {
    "segments": ["coffee_enthusiast", "high_purchase_intent"],
    "targeting_kvs": [
      { "key": "category_affinity", "value": "beverages" },
      { "key": "seasonal_relevance", "value": "high" }
    ]
  }
}
The buyer returns an offer per matched package. Each offer carries a package_id and optionally a brand, price, summary, creative_manifest, and macros. The summary gives the publisher enough to judge relevance. When the creative manifest is present inline, the publisher has everything needed to render. For large creatives (e.g., VAST video), the manifest references external assets via URLs rather than embedding them. The response also includes enrichment signals — audience segments and targeting key-values — that the publisher can pass to their ad server.

What Context Match never carries

  • User IDs (hashed or otherwise)
  • Device identifiers
  • Session tokens
  • IP addresses
  • Any data that could identify a specific user
This is not a policy restriction. It is enforced by the router’s context code path, which has no access to identity data — no shared memory, no shared state, no communication channel to the identity code path. TEE attestation can make this separation independently verifiable.

Identity Match

Separately — and with a random time delay — the publisher sends an Identity Match request to the TMP Router, which fans out to each buyer’s agent. The request contains an opaque user token and the list of packages to evaluate. It contains no page context.

What the publisher sends

{
  "type": "identity_match_request",
  "request_id": "id-9b2c",
  "user_token": "tok_hk82mfp1",
  "uid_type": "uid2",
  "consent": {
    "gdpr": true,
    "tcf_consent": "CPx2XYZABC..."
  },
  "package_ids": ["pkg-A", "pkg-B", "pkg-C"]
}
The user_token comes from an existing identity provider (ID5, LiveRamp, UID2) or is generated by the publisher. The uid_type tells the buyer which identity graph to resolve against, avoiding trial-and-error matching. The consent object carries the user’s consent signal — buyers in regulated jurisdictions MUST NOT process the token without it. The token is opaque to the buyer — the buyer can map it to their own identity graph (if they have a match), but cannot reverse it to PII or correlate it with any page context. Note what is absent: no URL, no search query, no content signals, no topic IDs. The buyer agent evaluates this request based purely on user identity and package eligibility.

What the buyer responds

{
  "type": "identity_match_response",
  "request_id": "id-9b2c",
  "eligible_package_ids": ["pkg-A", "pkg-B"],
  "ttl_sec": 60
}
The buyer reports that this user is eligible for packages A and B. Package C is absent — the user is not eligible. The publisher does not need to know why — frequency capping, audience mismatch, and other disqualification reasons are buyer-internal. The ttl_sec: 60 tells the router: “Cache this for 60 seconds.” The router uses this cached eligibility to fill whatever placements exist — a single slot, a CTV ad pod, or a page with multiple ad units — without re-querying the buyer. The publisher decides how to allocate across placements.

What Identity Match never carries

  • Page URLs
  • Content hashes
  • Search queries
  • Topic IDs
  • Content ratings
  • Any data that could identify what the user is looking at
This is enforced by the router’s identity code path, which has no access to context data — no shared memory, no shared state, no communication channel to the context code path. TEE attestation can make this separation independently verifiable.

The Publisher-Side Join

The publisher now has two independent responses. They join them on their own infrastructure — neither the buyer agents nor the router see the joined result.
For each offer from Context Match:

  pkg-A:
    Context says: offer for cold brew carousel (creative manifest inline)
    Identity says: eligible (in eligible_package_ids)
    → Accept the offer. Render using the inline creative manifest.
    → Apply "coffee_enthusiast" segment to ad server targeting.

  pkg-C:
    Context says: offer for iced beverage promotion
    Identity says: not eligible (absent from eligible_package_ids)
    → Skip this offer for this user.

Result: Accept Package A.
Set targeting KVs: category_affinity=beverages, seasonal_relevance=high.
The publisher already knows how to render Package A — it maps to a sponsored carousel slot. The inline creative manifest carries the catalog items and assets needed. The publisher handled the rest.

The Offer Model

TMP’s response model is the offer. An offer carries a package_id (required) and optional fields: brand, price, summary, creative_manifest, and macros (a key-value map for dynamic values). Simple case (GAM/Prebid): The offer carries package_id. The publisher flows package_id via targeting_kvs signals to GAM for line item matching. The macros map carries dynamic values (e.g., sponsor labels, promo text) that GAM can insert into the creative at render time. Rich case (AI assistants, dynamic retail): The offer includes a summary (“50% off cold brew — recipe integration”) so the publisher can judge relevance, and an inline creative_manifest with everything needed to render. For large creatives (e.g., VAST video), the manifest references external assets via URLs rather than embedding the full payload. Dynamic brands: When the product supports dynamic_brands, the buyer can include a brand on the offer, selecting from their portfolio at match time rather than being locked to a pre-configured package brand. Variable pricing: When the product supports variable pricing, the buyer can include a price on the offer. The creative manifest carries catalog items, text, images, and everything else needed for rendering. This reuses the existing CreativeManifest schema. Attribution tracking happens via delivery reporting or clean room integration, not via a real-time tracking token. The publisher reports which packages were delivered; attribution is reconciled out-of-band.

Enrichment Signals

Context Match responses can include enrichment signals alongside package activation:
  • Segments: Audience or contextual segments (e.g., “coffee_enthusiast”, “high_purchase_intent”) that flow into the publisher’s ad server as targeting signals.
  • Targeting key-values: Arbitrary key-value pairs (e.g., category_affinity=beverages) that the publisher can use for line item targeting, reporting breakdowns, or real-time decisioning.
Enrichment signals are additive — they are not tied to specific packages. A buyer agent might return enrichment signals even when it activates no packages, providing value as a data provider rather than a demand source. This is how existing RTD (Real-Time Data) modules work today. TMP unifies enrichment and package activation into a single protocol, so a buyer agent can do both in one response.

Package List Management

Identity Match sends ALL active package IDs for a given buyer — not just those that matched in Context Match. This is intentional: it prevents the buyer from correlating which packages matched the page content. If the buyer only received packages that matched a hiking article, they’d know the user was reading about hiking. Package lists update when new media buys are created via create_media_buy. The router maintains the active package list per buyer, derived from the buyer’s media buy history with the publisher. In Context Match, the optional package_ids field can narrow evaluation when the publisher has a specific reason — for example, CTV pod composition where only video packages apply. This does not affect Identity Match, which always sends ALL active packages. Stale packages from expired or cancelled media buys should be removed from the active list within 1 hour. The router is responsible for this cleanup.