Documentation Index
Fetch the complete documentation index at: https://docs.adcontextprotocol.org/llms.txt
Use this file to discover all available pages before exploring further.
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 two HTTP/2 endpoints under a single base URL — POST /context for Context Match and POST /identity for Identity Match. The router dispatches by path:
| Message type | Receives | Returns |
|---|
context_match_request | Page/content signals, placement, geo | Offers with creative manifests |
identity_match_request | Seller agent URL, identity tokens, optional package ID list | Eligible package IDs + serve_window_sec |
Each endpoint handles one message type. Both 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.
- Create media buys via
create_media_buy with the publisher’s sales agent
- Sync creatives via
sync_creatives so the publisher has your creative assets
- 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:
- Look up your active packages for this
property_rid and placement_id
- Evaluate each package’s targeting against the context signals, geo, and artifact refs
- 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 the seller’s seller_agent_url and one or more identity tokens. Use seller_agent_url to look up the active package set you have registered for that seller, resolve identities on whichever tokens you support, then check each package’s eligibility rules against the resolved user. The publisher MAY also send package_ids to scope evaluation explicitly; when present, evaluate against the intersection of your registered active set and package_ids, and silently drop any IDs you don’t recognize — both publisher modes (all-active and fuzzed/padded) rely on this behavior. Surfacing unknown IDs as errors would leak your registry membership back to the publisher.
// Request you receive
{
"type": "identity_match_request",
"request_id": "id-9c4e",
"seller_agent_url": "https://publisher.example",
"identities": [
{ "user_token": "opaque-token-abc123", "uid_type": "publisher_first_party" },
{ "user_token": "ID5*7xYp...", "uid_type": "id5" }
],
"package_ids": ["acme-outdoor-q2", "acme-winter-clearance", "acme-loyalty-retarget"],
"consent": { "gdpr": true, "tcf_consent": "CPxyz..." }
}
What you do:
- Resolve the tokens against your identity graph on whichever
uid_types you support. Entry order in the request is not semantically significant — apply your own preference order per the buyer’s own preference order. All entries identify the same user, so the first successful resolution is sufficient. When multiple identity types are present, buyers SHOULD prefer opaque provider IDs (UID2, EUID, ID5, RampID) over strongly re-identifying tokens like hashed_email; this neutralizes scenarios where a misconfigured or compromised router forwards only the highest-risk token.
- Check frequency caps: has this user exceeded the package’s impression limit?
- Check audience rules: is this user in the target audience?
- Check suppression lists: should this user be excluded?
- Return eligibility for each package
// Your response
{
"type": "identity_match_response",
"request_id": "id-9c4e",
"eligible_package_ids": ["acme-outdoor-q2", "acme-loyalty-retarget"],
"serve_window_sec": 60
}
Return only the package IDs that pass your eligibility checks. Packages not in the list are treated as ineligible. The serve_window_sec is a per-package single-shot fcap: after the publisher serves the user one impression on each eligible package within this window, the publisher MUST re-query Identity Match before serving from those packages again. Default 60s, max 300s. This is not a router response cache TTL — see The serve-window contract.
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:
- Intersects your Context Match offers with your Identity Match
eligible_package_ids
- Only activates packages that appear in both responses
- Sets ad server targeting key-values for matched packages
- 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. Cap policy and counting live in your impression tracker; the Identity Match service consumes only cap-fire signals at query time. The split:
- Impression tracker receives pixel fires, decodes the TMPX token, and applies whatever fcap policies you maintain — counting impressions across whatever dimensions you cap on (package, campaign, advertiser, creative, line item) for each resolved user identity, with whatever windowing and dedup logic your policy engine uses.
- On the impression that exhausts a cap, the impression tracker writes a cap-fire entry —
(user_identity, package) capped until <expireAt> — into the Identity Match cap-state store.
- Identity Match service at query time excludes any package with a cap-fire entry against any of the request’s identities from
eligible_package_ids.
The protocol does not constrain how you count impressions, where policies live, or how you dedup across identities. It only defines the boundary: cap-fire events flow into the cap-state store; the IdentityMatch service checks presence at query time. See Frequency-Cap Data Flow for the boundary contract and the reference cap-state store.
When an fcap rule changes — a window shortens or lengthens, a max_count rises or falls, a policy is paused or removed, a package is reassigned — you MUST re-evaluate the affected (user_identity, package) cap-state entries against the new policy and push the appropriate updates: delete entries for users no longer over-cap, extend (overwrite with a new expire_at) entries that are still over-cap but whose window changed. The cap-state store doesn’t store counts and can’t re-evaluate on its own; the buyer’s policy owner is the source of truth. See Policy updates and cap-state re-evaluation for the event shapes.
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.
How Buyers Learn About Exposures
The tmpx field on the Identity Match response carries a TMPX token — an HPKE-encrypted blob containing the user’s resolved identity tokens. The publisher substitutes {TMPX} into creative tracking URLs. When the ad serves, your impression pixel receives the encrypted token. Your impression tracker decrypts it, applies your fcap policy logic against the resolved identities, and (when a cap fires) writes a cap-fire entry to the Identity Match cap-state store. Most production deployments separate decode (synchronous, at intake) from policy evaluation and cap-state writes (asynchronous, behind a queue) for buffering.
This gives you real-time per-user exposure signals without the publisher seeing user identity.
See TMPX Exposure Tokens for the encryption format and binary token structure, and Frequency-Cap Data Flow for the cap-state store boundary contract.
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 base 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 endpoints. The publisher then configures your provider entry in their router (see router deployment).
{
"provider_id": "acme-outdoor-us",
"endpoint": "https://us.tmp.acmeoutdoor.example/v1",
"context_match": true,
"identity_match": true,
"countries": ["US"],
"uid_types": ["uid2", "rampid", "id5"]
}
When you support identity_match, you MUST declare countries (which country codes you serve) and uid_types (which identity types you resolve). The router uses these to filter Identity Match fan-out — without them, the router cannot route requests to your provider.
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.
Health endpoint
You SHOULD expose GET /health at your base URL. Return HTTP 200 with {"status": "ok"} when ready. The publisher’s router uses this for pre-flight checks and monitoring. It is not called during request fan-out — only on a background interval.
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 serve-window contract
The serve_window_sec field on Identity Match responses is a per-package single-shot fcap between the buyer and the publisher:
- For each package in
eligible_package_ids, the publisher MAY serve the user at most one impression on that package within serve_window_sec seconds.
- After the publisher has served one impression on each eligible package, the publisher MUST re-query Identity Match before serving any of those packages to the same user again.
- Multi-impression frequency capping (5/day, 100/month, etc.) is separate. It lives in your buyer-side state and is updated out-of-band via TMPX impression callbacks regardless of
serve_window_sec. The serve window is the protocol-level throttle; multi-impression caps are buyer-internal policy.
The router MAY apply an internal deduplication cache keyed by {identities_hash, provider_id, package_ids_hash, consent_hash} (see spec for canonical bytes), but the publisher’s binding contract is the serve-window throttle, not the router’s cache window.
Choosing a serve_window_sec value: Default 60 seconds. Range 1–300. Anything longer than 300 makes per-package fcap too coarse for typical campaigns. Anything shorter than your IdentityMatch round-trip just adds load. 60 is a good default; tune downward if eligibility state shifts faster (close to a cap, audience just changed) or upward (max 300) if your IdentityMatch service is at load and the campaigns are tolerant of coarser fcap.
| Metric | Target |
|---|
| Agent-side processing | < 30ms p95 |
| End-to-end (publisher → router → agent → router → publisher) | < 50ms p95 |
| Availability | 99.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.
Buyers receive real-time per-user exposure signals via the {TMPX} macro. The Identity Match response includes an encrypted TMPX token that flows through creative tracking URLs to your impression pixel. Your cluster master decrypts the token and updates per-user frequency state in real time. get_media_buy_delivery provides aggregate delivery metrics for reconciliation and pacing — it is not the primary frequency input.
What’s Different from OpenRTB
| OpenRTB | TMP |
|---|
| You receive | Full bid request (user + content + device) | Either content OR identity, never both |
| You return | Bid price | Offer (creative manifest) or eligible package IDs + serve window |
| Auction | Exchange runs auction | No auction — publisher joins locally |
| Frequency | Per-DSP only | Cross-publisher via Identity Match |
| Integration | Per-exchange SSP adapter | Two endpoints (context + identity), any surface |