Skip to main content

TMP for Buyer Agents

As a buyer agent, you receive Context Match and Identity Match requests from TMP Routers and respond with offers and eligibility decisions. You never send requests — publishers initiate every interaction through their router.

What You Build

A buyer agent exposes a single HTTP/2 endpoint. The router sends JSON POST requests with a type field that distinguishes message types:
Message typeReceivesReturns
context_match_requestPage/content signals, placement, geoOffers with creative manifests
identity_match_requestOpaque user token, all active package IDsEligible package IDs + TTL
Your handler dispatches on the type field. Both message types must respond in under 50ms. The router enforces this budget and will skip slow providers. The adcp-go SDK provides Go types, request parsing, and response builders for both endpoints.

Prerequisites

Before TMP requests arrive, your packages must exist. A media buy contains one or more packages — TMP operates at the package level.
  1. Create media buys via create_media_buy with the publisher’s sales agent
  2. Sync creatives via sync_creatives so the publisher has your creative assets
  3. Register as a TMP provider so the publisher’s router knows your endpoints
The router learns about your packages from the publisher’s deal history. You don’t push package lists to the router.

Responding to Context Match

The router sends you page context. You evaluate your active packages against that context and return offers for packages that match.
// Request you receive
{
  "type": "context_match_request",
  "request_id": "ctx-8f3a2b",
  "property_rid": "01916f3a-9c4e-7000-8000-000000000010",
  "property_type": "website",
  "placement_id": "article-sidebar",
  "artifact_refs": [
    { "type": "url", "value": "https://streamhaus.example/articles/hiking-gear-2026" }
  ],
  "context_signals": {
    "topics": ["550", "710"],
    "keywords": ["hiking", "gear", "outdoor"],
    "sentiment": "positive"
  },
  "geo": { "country": "US", "region": "US-CO" }
}
What you do:
  1. Look up your active packages for this property_rid and placement_id
  2. Evaluate each package’s targeting against the context signals, geo, and artifact refs
  3. Return offers for packages that match, each with a creative manifest
// Your response
{
  "type": "context_match_response",
  "request_id": "ctx-8f3a2b",
  "offers": [
    {
      "package_id": "acme-outdoor-q2",
      "brand": { "domain": "acmeoutdoor.example.com" },
      "summary": "Hiking gear seasonal promotion",
      "creative_manifest": {
        "format_id": { "agent_url": "https://streamhaus.example", "id": "sidebar_display" },
        "assets": {
          "headline": { "content": "Trail-ready gear for every summit" },
          "image": { "url": "https://cdn.acme.example/hiking-hero.jpg", "width": 300, "height": 250 },
          "cta": { "content": "Shop now" }
        }
      },
      "price": { "amount": 12.50, "currency": "USD", "model": "cpm" }
    }
  ]
}
What you never receive in Context Match: user IDs, device IDs, session tokens, IP addresses, or cookies. You cannot identify the user.

Responding to Identity Match

The router sends you an opaque user token and a list of ALL your active package IDs for this publisher. You check each package’s eligibility rules against the token.
// Request you receive
{
  "type": "identity_match_request",
  "request_id": "id-9c4e",
  "user_token": "opaque-token-abc123",
  "uid_type": "publisher_first_party",
  "package_ids": ["acme-outdoor-q2", "acme-winter-clearance", "acme-loyalty-retarget"],
  "consent": { "gdpr": true, "tcf_consent": "CPxyz..." }
}
What you do:
  1. Resolve the opaque token against your identity graph (if you have one)
  2. Check frequency caps: has this user exceeded the package’s impression limit?
  3. Check audience rules: is this user in the target audience?
  4. Check suppression lists: should this user be excluded?
  5. Return eligibility for each package
// Your response
{
  "type": "identity_match_response",
  "request_id": "id-9c4e",
  "eligible_package_ids": ["acme-outdoor-q2", "acme-loyalty-retarget"],
  "ttl_sec": 60
}
Return only the package IDs that pass your eligibility checks. Packages not in the list are treated as ineligible. The ttl_sec tells the router how long to cache this response — during that window, the router returns cached eligibility without re-querying you. The publisher uses cached eligibility to allocate across whatever placements exist. Set the TTL based on how quickly your eligibility state changes (frequency caps, audience updates, etc.). What you never receive in Identity Match: page URLs, content topics, keywords, article text, or any content signal. You cannot determine what the user is looking at. Why you receive ALL packages, not just the ones that matched in Context Match: this prevents you from inferring what content the user is viewing. If you only received packages that matched the hiking article, you’d know the user was reading about hiking. Receiving all packages preserves structural separation.

The Join Happens Publisher-Side

You never see the combined result. The publisher’s router:
  1. Intersects your Context Match offers with your Identity Match eligible_package_ids
  2. Only activates packages that appear in both responses
  3. Sets ad server targeting key-values for matched packages
  4. The ad server makes the final rendering decision
You have no role in this step. The publisher controls activation.

Frequency Cap Management

Cross-publisher frequency capping is the primary use case for Identity Match. Your agent maintains frequency state per user token:
  • Count impressions by user token + package ID
  • Track recency — when was the last impression for this token?
  • Apply caps from the media buy: max_impressions per window, minimum recency between exposures
  • Exclude the package from eligible_package_ids when a cap is hit
  • Set ttl_sec to reflect how long this eligibility is valid — a shorter TTL means the router re-checks sooner, which is useful when a cap is close to being reached
Because Identity Match runs across all publishers using TMP, a user who saw your ad on Publisher A will correctly show as over-frequency on Publisher B — even though you can’t see which publisher sent the request.

Provider Registration

Provider registration is an out-of-band process. After establishing a media buy via create_media_buy, coordinate with the publisher to provide your TMP endpoint URL. This typically involves a commercial agreement and may require legal review, since the publisher will be sending content signals and identity tokens to your endpoint. The publisher then configures your provider entry in their router (see router deployment).
{
  "provider_id": "acme-outdoor-buyer",
  "endpoint": "https://tmp.acmeoutdoor.example/v1",
  "context_match": true,
  "identity_match": true
}
You can support either or both endpoints. Context Match only means contextual targeting without frequency capping. Identity Match only means the publisher evaluates context locally from your media buy’s targeting rules and calls you only for frequency checks. Both means full TMP integration.

Error Handling

When your agent cannot evaluate a request, return an error response:
{
  "type": "error",
  "request_id": "ctx-8f3a2b",
  "code": "provider_unavailable",
  "message": "Targeting data temporarily unavailable"
}
Common scenarios:
  • No matching packages: Return an empty offers array (not an error). This is the normal case when your packages don’t match the content.
  • Internal failure: Return an error response. The router skips your provider and proceeds with other providers.
  • Timeout: If you can’t respond within the latency budget, the router skips you. No error response needed — the router handles this.

The TTL Caching Contract

The ttl_sec field on Identity Match responses is a caching contract between the buyer and the router:
  • The router caches the response for ttl_sec seconds, keyed by {user_token, provider_id, hash(package_ids)}
  • During that window, the router returns cached eligibility without re-querying the buyer
  • The publisher uses cached eligibility to allocate across whatever placements exist — a single pre-roll, a CTV ad pod, or a web page with multiple ad units
  • The buyer doesn’t need to know how many placements exist or how the publisher allocates
Choosing a TTL: Set the TTL based on how quickly your eligibility state changes. If frequency caps reset hourly, a 300-second TTL is reasonable. If a user is close to a cap limit, return a shorter TTL (e.g., 30 seconds) so the router re-checks sooner.

Performance Requirements

MetricTarget
Agent-side processing< 30ms p95
End-to-end (publisher → router → agent → router → publisher)< 50ms p95
Availability99.9%
Error rate< 0.1%
The 30ms agent-side budget accounts for network overhead between the router and your endpoint. The router tracks your latency percentiles and adaptively adjusts your timeout allocation. Consistently slow responses result in the router reducing your allocation or skipping your provider.

Measurement

The publisher reports delivery via get_media_buy_delivery. Your agent queries delivery data to reconcile impressions, track pacing, and update frequency state. TMP does not define an impression callback — the buyer learns about impressions through delivery reporting, not real-time pixel fires. Update your exposure store from delivery reports to keep cross-publisher frequency caps current.

What’s Different from OpenRTB

OpenRTBTMP
You receiveFull bid request (user + content + device)Either content OR identity, never both
You returnBid priceOffer (creative manifest) or eligible package IDs + TTL
AuctionExchange runs auctionNo auction — publisher joins locally
FrequencyPer-DSP onlyCross-publisher via Identity Match
IntegrationPer-exchange SSP adapterSingle endpoint, any surface