Managed ad networks (e.g. networks operating hundreds or thousands of publisher domains) already distributeDocumentation Index
Fetch the complete documentation index at: https://agenticadvertisingorg-changeset-release-main.mintlify.app/llms.txt
Use this file to discover all available pages before exploring further.
ads.txt via HTTP redirects or centralized hosting. adagents.json supports the same scale through a built-in delegation model: the URL reference pattern.
This guide maps your existing ads.txt deployment knowledge to adagents.json and covers the infrastructure patterns that work at network scale.
How it compares to ads.txt distribution
Bothads.txt and adagents.json require a file at a well-known path on each publisher origin. The deployment mechanics are similar, but adagents.json has a built-in delegation model that replaces the HTTP redirect patterns networks typically use for ads.txt.
| Concern | ads.txt | adagents.json |
|---|---|---|
| File location | /ads.txt | /.well-known/adagents.json |
| Delegation mechanism | HTTP 301/302 redirect | authoritative_location field (in-file reference) |
| What the delegation expresses | ”This file lives somewhere else" | "This publisher delegates to a named authority” |
| Publisher intent | Ambiguous (redirect could be infrastructure) | Explicit (pointer file is a declaration) |
| Scope of authorization | Flat (DIRECT / RESELLER) | Structured (property, placement, country, time window, delegation type) |
| Caching at scale | Each domain cached independently (no deduplication) | Validators cache one authoritative file for all domains that reference it |
| File format | Plain text, one entry per line | JSON with schema validation |
authoritative_location field makes delegation an explicit publisher declaration.
The pointer file pattern
Each managed domain hosts a minimal pointer file at/.well-known/adagents.json. The pointer references one centralized authoritative file that the network maintains.
Pointer file (on each domain):
last_updated timestamp in the pointer file reflects when the pointer itself was last modified (e.g., when the authoritative_location URL changed), not when the authoritative file was updated. The authoritative file carries its own last_updated.
Authoritative file (at the network):
How validators resolve pointer files
The validator fetches the pointer file, follows theauthoritative_location URL, and validates the authoritative file as a normal inline structure.
Single hop only. The authoritative file must not itself contain an authoritative_location. This prevents redirect chains and infinite loops.
One authoritative file vs. per-publisher files
The example above shows every domain pointing to the same authoritative file. This works when all publishers share the same agents, delegation types, and placement structure. Per-publisher authoritative files make sense when arrangements differ across the network:- Different publishers authorize different agents (some have their own direct sales alongside the network)
- Different delegation types (Publisher A is
ad_networkonly, Publisher B retains adirectpath for premium placements) - Different placement structures (one publisher has
pre_rollandhost_read, another only hasdisplay_banner) - Different governance vendors in
property_features
Why not HTTP redirects?
HTTP redirects work forads.txt because ads.txt is a flat list with no self-referential semantics. Crawlers follow the redirect chain and validate the final file.
For adagents.json, HTTP redirects cause problems:
- Ambiguous intent. A redirect could mean delegation, infrastructure migration, or CDN routing. The pointer file explicitly declares delegation.
- No scoping. An HTTP redirect is all-or-nothing. A pointer file sits alongside a structured authorization model where the network can declare exactly what it is authorized to sell.
- Caching penalty. With HTTP redirects, a validator has no way to know that 10,000 domains all redirect to the same file. It must treat each response as independent — 10,000 cache entries for identical content. With
authoritative_location, the validator sees the same URL across all pointer files and caches the authoritative file once. For a network with thousands of domains, this is the difference between one cache entry and thousands.
/.well-known/adagents.json are not prohibited, but they are not the recommended pattern. Use authoritative_location instead.
Choosing the right delegation type
When a network authorizes agents on behalf of managed publishers, thedelegation_type field describes the commercial relationship:
delegation_type | Use when | Example |
|---|---|---|
direct | Publisher treats this as their own sales channel, even though the network operates it | A white-label sales agent branded as the publisher |
delegated | Publisher authorizes the network to sell on their behalf | A rep firm with explicit publisher agreements |
ad_network | Inventory is sold through the network’s package, not as the publisher’s endpoint | Mediavine-style managed network selling across its portfolio |
ad_network. Use delegated when individual publishers maintain their own commercial identity but authorize the network to represent them. Use direct only when the network operates what publishers present as their own sales infrastructure.
A single authoritative file can mix delegation types — different agents can have different relationships with the same inventory:
Keeping the file efficient with property tags
A managed network with 500 properties and three authorized agents could list every property ID in every agent entry — but that means maintaining 1,500 property-to-agent mappings. Property tags eliminate that redundancy. The principle: list each property once with its identifier and tags, then authorize agents by tag.properties with the right tags — no authorization entries need to change. When a new vertical agent comes on, add one agent entry with the relevant tag.
This keeps the file readable, maintainable, and compact even at thousands of properties.
Representing many publishers under a single authorization
The pattern above is for a single publisher’sadagents.json listing its own properties. Managed networks that represent many other publishers (WordPress networks, content-recommendation networks, multi-property holding-company configurations) host one authoritative file that declares which agents are authorized to sell on each represented publisher’s behalf.
When every represented publisher delegates the same agent under the same tag predicate — the canonical managed-network shape — use the compact publisher_domains[] form of publisher_properties:
managerdomain ads.txt fallback. The compact form scales linearly with the number of distinct selector predicates — typically a small constant — instead of with the number of represented publishers.
Controlling what each agent can sell with placements
Unlikeads.txt, where every authorized seller appears to have access to everything, adagents.json lets networks declare exactly which placements each agent is authorized to sell. This preserves sales leverage — buyers can see that premium inventory is only available through specific paths.
Define placements once at the top level, tag them for grouping, then scope each agent’s authorization to specific placement tags:
Additional authorization qualifiers
Beyond property tags and placement tags, agents can be scoped with:countries— restrict by geography (e.g.["US", "CA"])effective_from/effective_until— time-bounded authorization for seasonal or trial arrangementsexclusive— declare whether this is the sole authorized path for the scoped inventory
What publishers are authorizing
When a publisher’s domain hosts a pointer file, they are declaring that the authoritative file speaks for them. This means:- The agents listed in the authoritative file are authorized to sell the publisher’s inventory
- The
delegation_typeon each agent entry describes the commercial relationship - Qualifiers (
placement_tags,countries,exclusive, etc.) scope what each agent can sell
Deployment patterns
All of these patterns accomplish the same thing: serve a static JSON pointer file at/.well-known/adagents.json on each managed domain. Choose based on your existing infrastructure.
CDN edge function
Serve the pointer file from a CDN worker or edge function. This is the most common pattern for networks that already manage DNS and CDN for their publishers. Cloudflare Worker:CMS plugin
For networks managing WordPress or similar CMS installs, a plugin can serve the pointer file without touching server configuration. WordPress (mu-plugin):wp-content/mu-plugins/ across managed installs. Must-use plugins load automatically without activation.
CI/CD pipeline
Generate pointer files from a central configuration and deploy them as static assets alongside each site. GitHub Actions example:DNS + edge function
If the network manages DNS but not the origin servers, route only the well-known path through an edge function using a CNAME and path-based routing. This works when:- The publisher controls their own origin server
- The network manages DNS (common in managed network agreements)
- You need to serve
adagents.jsonwithout modifying the publisher’s server
/.well-known/adagents.json requests to a network-operated edge function (using the CDN edge pattern above), while passing all other traffic through to the publisher’s origin.
The specific configuration depends on your DNS and CDN provider. The principle is the same: intercept the well-known path, serve the pointer file, pass everything else through.
Validating deployment
Single domain
Using the AdCP client
The@adcp/sdk package includes a network consistency checker that validates deployment across all managed domains and detects common failure modes:
Integrating with CI/CD
Run the consistency checker after deploying pointer files to catch issues before they affect buyers:Troubleshooting
Five failure modes that occur in managed network deployments, how to detect them, and how to fix them.Orphaned pointer
What happened: A publisher domain has a pointer file referencing your authoritative URL, but the authoritative file doesn’t list that domain in itsproperties.
How it looks: A buyer agent fetches cookingdaily.com/.well-known/adagents.json, follows the pointer to the network’s authoritative file, and finds no property with publisher_domain: "cookingdaily.com". The domain appears to delegate to a network that doesn’t claim it.
Common cause: The network removed the publisher from the authoritative file (e.g., contract ended) but the pointer file on the domain was not removed.
Fix: Either re-add the property to the authoritative file, or remove/replace the pointer file on the publisher’s domain. If the network no longer manages the domain’s DNS or CDN, coordinate with the publisher to remove the pointer.
Detection: npx adcp check-network --url <authoritative_url> reports these as orphaned pointers.
Stale pointer
What happened: A publisher’s pointer file still references the network’s authoritative URL after the relationship ended. Similar to an orphaned pointer, but from the publisher’s perspective — the domain still claims delegation to a network that no longer authorizes it. Common cause: Network terminated the publisher but doesn’t control the domain’s infrastructure. The publisher hasn’t updated their well-known path. Fix: The publisher must update or remove their pointer file. The network should notify the publisher when removing them from the authoritative file. The AAO registry detects this mismatch during crawls and surfaces it in network health monitoring.Missing pointer
What happened: A domain is listed in the authoritative file’sproperties (via publisher_domain), but /.well-known/adagents.json on that domain either doesn’t exist or doesn’t point to the expected authoritative URL.
How it looks: The network claims to represent the domain, but the domain doesn’t confirm delegation. Buyer agents cannot verify the authorization chain.
Common cause: Publisher recently joined the network but the pointer file hasn’t been deployed yet, or the deployment failed.
Fix: Deploy the pointer file to the domain using one of the deployment patterns above. Verify with:
Schema errors
What happened: The authoritative file has validation errors — malformed JSON, missing required fields, invalid field values. Impact: One bad deploy breaks validation for every domain in the network, since they all reference the same file. Fix: Validate the authoritative file before deploying:Agent endpoint unreachable
What happened: Anauthorized_agents entry’s URL doesn’t respond or returns errors. Buyer agents cannot reach the sales agent declared in the authorization.
Common cause: Agent service is down, URL changed, or DNS is misconfigured.
Fix: Verify the agent endpoint is reachable and returns a valid agent card:
check-network command validates all agent endpoints and reports response times, so you can catch slow or failing agents before buyers do.
Security considerations
One deploy to the authoritative file changes authorization across every publisher in the network. That scale is the point, and it’s also the blast radius — a compromised network CDN can authorize a malicious sales agent across thousands of domains simultaneously. Two concrete implications that go beyond schema correctness: Validator fetch semantics. The authoritative URL points at a network-controlled origin. Without explicit fetch rules, a misbehaving origin can poison caches or hang validators. Validators MUST:- Connect only over HTTPS with valid certificates, and refuse to follow redirects (a redirect changes the declared location — treat as an error).
- Cap response size with a two-tier policy: pointer files served at
/.well-known/adagents.json(whether inline or carryingauthoritative_location) keep the general SSRF body cap of 5 MB — a per-publisher pointer file should be tiny, and 5 MB catches misconfiguration. Authoritative files reached by dereferencingauthoritative_location(the second hop) use a higher recommended cap of 20 MB, because that origin has explicitly opted in to fanning out across a publisher network and routinely needs to enumerate thousands of properties or publisher domains. Enforce short connect/read timeouts (≤ 10s each) on both hops. - On 5xx or timeout, serve the previously cached authoritative file for up to 24 hours rather than failing closed. A transient CDN outage is not a revocation.
- Attempt a refresh at least every 24 hours. Repeated 5xx responses MUST NOT extend the cache — the 7-day absolute cap is measured from the most recent successful fetch, not from the most recent response of any kind.
- Cap cached lifetime at 7 days from the most recent successful fetch, regardless of the origin’s
Cache-Control. After that, fail closed — the network has had seven days to fix its origin. - Treat a non-monotonic
last_updated(the refreshed file’s timestamp is older than the cached file’s) as an invalid response, equivalent to a 5xx: serve the cache, alert, do not adopt the older file. This blocks rollback attacks where an attacker re-serves a stale file to reinstate a previously revoked agent.
- Origins SHOULD emit
ETagandLast-Modifiedheaders on the authoritative file, regenerated whenever the file content changes. - Validators SHOULD send
If-None-Match(preferred) orIf-Modified-Sinceon every refresh and treat a304 Not Modifiedas “cache stays valid, restart the 7-day clock from this success” — same effect as a successful body fetch for cache-lifetime purposes. - The per-
authorized_agents[]optionallast_updatedfield (seeadagents.jsonschema) gives consumers a second axis for partial-walk indexing: a validator that already has an index keyed by the file-levellast_updatedcan skip authorized-agents entries whose ownlast_updatedis older than the indexed value. This is advisory — consumers MAY ignore it and re-index the full file.
Publisher revocation (the exit lifecycle)
Removing a publisher domain frompublisher_properties[].publisher_domains[] alone is not enough — cached authoritative files at downstream validators will keep authorizing the departed publisher for up to the 7-day cache cap. For revocations that need to propagate on the next refresh (a publisher leaving a network in dispute, a compliance issue, a misconfiguration cleanup), the authoritative file MUST list the publisher in the top-level revoked_publisher_domains[] array with a revoked_at timestamp.
Validator behavior:
- Validators MUST treat any publisher domain in
revoked_publisher_domains[]as no-longer-authorized, regardless of whether the same domain still appears in anyauthorized_agents[].publisher_properties[].publisher_domain/.publisher_domains[], inauthorized_agents[].properties[].publisher_domain(inline_propertiesauthorization type), or in top-levelproperties[].publisher_domainelsewhere in the file. The revocation list takes precedence — this lets a network ship a revocation without redeploying every selector entry. - Validators with a cached prior version of the file MUST apply the revocation as of the validator’s
last_updatedadoption time, not the entry’srevoked_at(which can be in the past for catch-up entries). - Append-only durability on the validator side. A
publisher_domainthat a validator has ever observed in arevoked_publisher_domains[]entry MUST be held as revoked for 7 days from the earliestrevoked_atthe validator has ever observed for that domain, even if the entry is missing from a subsequent fetch. Durability keys onpublisher_domainalone — keying on the(publisher_domain, revoked_at)tuple would let an attacker re-emit the same domain with a slightly mutatedrevoked_at(one second earlier, say) and present a “fresh” tuple the validator has never observed, bypassing the hold. Userevoked_atonly to set the clock origin, not to identify the durability key. This places durability on the validator’s cached state rather than on the network’s retention SHOULD, and closes the rollback gap: an attacker who re-serves a stale file withrevoked_publisher_domains[]removed andlast_updatedadvanced cannot reinstate a previously-revoked publisher inside the validator’s 7-day window. New revocations seen for the first time in the current fetch (no prior observation) start the 7-day clock from now. - Persistence across restart. Validators SHOULD persist observed revocation entries (
{publisher_domain, earliest_revoked_at, first_observed_at}) to durable storage. An in-memory-only validator that restarts inside the 7-day window MAY accept a rolled-back file because it has no record of the prior observation; operators SHOULD treat this as a known limitation and either persist the index or accept the residual risk. The 7-day window is measured from the validator’s first observation, not from process start. - The network SHOULD retain each
revoked_publisher_domains[]entry for at least 7 days afterrevoked_atso validators that didn’t observe the entry during its first appearance still pick it up on the next refresh.
reason: "relationship_ended" so validator change-detection can suppress alerts on routine revocations and route the rest for review.
Re-authorizing a previously-revoked publisher. There is no revoked_until field and no un-revoke verb. To re-authorize, the network removes the entry from revoked_publisher_domains[] after the validator-side 7-day durability window has elapsed since revoked_at. Removing the entry sooner is a no-op on validators that observed the original revocation (they hold the revocation locally for the remainder of the window). For time-bounded compliance pulls where same-week reinstatement is operationally required, prefer running the revocation as a reason: "compliance_violation" and coordinating reinstatement out of band; the schema deliberately does not give the network a re-authorize-before-7-days back door, which would be the same surface as a rollback attack.
Extension fields on revocation entries have no normative effect. revoked_publisher_domains[] items use the project-wide additionalProperties: true policy, but validators MUST ignore unknown fields on these entries — extensions cannot loosen revocation semantics or carry side-channel reinstatement signals.
Propagation latency. Validators that maintain an in-memory authorization index keyed by (agent, publisher_domain) MUST NOT continue to authorize a pair more than one crawl interval after the validator has successfully refetched an adagents.json that lists the publisher in revoked_publisher_domains[]. Cache-bounded staleness from the fetch semantics above (24-hour fallback on 5xx, 7-day absolute cap) applies — the bound holds against the most recent successful fetch, not against any arbitrary wall-clock time. Crawl backlog and intermediate CDN caching extend the practical latency; the requirement is on the validator’s own pipeline, not on the network.
Validators SHOULD also apply revocation synchronously during manifest ingest, not only on the next crawl cycle: when adopting any adagents.json whose revoked_publisher_domains[] lists any publisher domain — regardless of whether the validator currently authorizes any agent for it — the validator SHOULD remove every (agent, publisher_domain) entry it holds for that publisher from the in-memory index before serving the next authorization decision. This composes with, does not replace, the append-only durability rule above: the in-memory index update is what keeps live queries fresh; the 7-day hold is what survives a rollback attack.
Reference implementation (non-normative). Other validators MAY satisfy the requirements above through different mechanisms. The chain in this repo is: (a) the writer’s revocation branch retires the matching catalog rows; (b) the next crawl pass re-snapshots the (agent, publisher_domain) set from the catalog (excluding retired rows); (c) the pre-vs-post diff emits an authorization.revoked event for every dropped pair; (d) the registry-sync consumer applies the event to the in-memory index. The authorization.revoked event shape is reference-implementation vocabulary, not a normative wire format.
Change detection. Because one deploy affects every publisher, buyer agents and validators SHOULD store the previous authoritative file and diff on each refresh, alerting on outlier changes. Concrete default thresholds that partners can implement on day one: any newly added authorized_agents entry that was not present in the previous fetch, any delegation-type downgrade (for example, an exclusive entry becoming non-exclusive), any property-count decrease greater than 10% or 50 absolute properties, and any change to authoritative_location itself. Tune from there — the goal is to catch a compromised deploy before it routes spend, not to suppress routine updates.
Pointer integrity (the per-publisher swap threat). The Validator fetch semantics and Change detection rules above defend against compromise of the network-side authoritative file and origin. They do not defend against compromise of the pointer file itself at a single publisher’s edge. An attacker who gains write access to one publisher’s /.well-known/adagents.json — via that publisher’s CDN control plane, origin storage, or DNS — can silently change the authoritative_location to an attacker-controlled URL. TLS on that URL is valid because the attacker is serving from infrastructure the publisher’s domain resolves to, the size/redirect/timeout caps do not trigger, and the change reads to a validator as a legitimate delegation handoff.
This last property is what makes pointer-swap distinct from a generic integrity failure: the whole point of the authoritative_location pattern is that publishers are allowed to change where they delegate, so the validator cannot treat any pointer change as adversarial without breaking legitimate delegation handoffs. The network-CDN threat is wide-and-shallow (one compromise, every publisher hijacked); the pointer-swap threat is narrow-and-deep (one publisher hijacked, but through a surface the network cannot monitor). Both are in scope.
Validators MUST treat a changed authoritative_location as a high-severity event, not a routine refresh. Concretely:
- Validators MUST NOT auto-adopt a changed
authoritative_location. Continue serving the previously cached authoritative file (subject to the 7-day cap above) while the change is under confirmation. This is the minimum normative floor; the SHOULDs below specify how confirmation is obtained. - Validators SHOULD honor the new location only after either (a) out-of-band confirmation — operator acknowledgement, a publisher support-channel notice, or an announced network transition — or (b) a minimum stability grace window of 24 hours during which the new pointer value must remain unchanged. The 24 h window is a fallback for the unconfirmed path; an out-of-band confirmation completed in minutes satisfies (a) and is compliant — validators MUST NOT impose a 24 h floor on the OOB path.
- “Announced network transition” in (a) means a publisher-attested or network-attested statement the validator operator can verify (e.g., a signed announcement countersigned by an existing trusted key, an operator-verified update to the publisher’s
brand.jsonagents[]set, or a notice on an established publisher-identity channel the operator already trusts for that publisher). A blog post or press release by itself does not qualify; the bar is verifiability, not publicity. - Validators SHOULD cross-check the candidate authoritative file against the publisher’s
/.well-known/brand.jsonwhen one is published. Ifbrand.jsondeclaresagents[], the candidate authoritative file’sauthorized_agents[]URLs SHOULD reconcile with thebrand.jsonagent set. An authoritative file that authorizes sales agents absent from the publisher’s own identity declaration is a strong signal of pointer compromise and SHOULD block adoption pending operator review. During a legitimate inter-network migration thebrand.jsonagents[]set can lag the pointer change; whenbrand.json’slast_updatedis older than the pointer file’slast_updated, treat abrand.json/authoritative mismatch as stale cross-check rather than authoritative contradiction, and fall back to path (a) or (b) above to confirm the migration. - Refuse adoption on mixed signals: a pointer change coincident with a
last_updatedregression on the candidate authoritative file, a domain-wide delegation-type downgrade, or a first-seen sales agent with no prior ecosystem history is grounds to hold the cache and alert rather than to adopt. For this rule, regression means the candidate file’slast_updatedis strictly earlier than the cached file’slast_updatedby more than a small clock-skew tolerance (recommended: 60 seconds); pointer files served from multiple edges can observe minor non-monotonicity under normal operation, and the regression check is for rollback attacks, not clock jitter.
/.well-known/brand.json, DNS records, TLS certificate issuance). A pointer file is an identity declaration; treating it as a static marketing asset is the misconfiguration that makes the swap threat practical.
Relationship termination. The pointer-file pattern relies on the network controlling publisher DNS or edge. When the relationship ends, both sides of the delegation must come down together:
- The network MUST remove the publisher from the authoritative file’s
propertiesat termination, even when it has already lost DNS/edge control. A publisher still pointing at an ex-network’s file with no matching property becomes an orphaned pointer — visible to buyers as unauthorized. - Validators SHOULD re-fetch and re-validate when a publisher domain transfers ownership, rather than relying on cached delegation.
(authoritative_location, last_updated) object, with the public key anchored out-of-band — publisher-attested in brand.json, or via the future centralized publisher-key registry. The signing primitive and key-discovery / rotation model require an agreed design and are tracked as a planned AdCP 4.0 addition, not a 3.x requirement. To keep the 4.0 rollout viable, implementers publishing pointer files today SHOULD keep the pointer object shape stable: the top-level object SHOULD contain only authoritative_location and last_updated, with no additional top-level fields, so a detached signature can later be carried in a sibling field (or a .sig companion path) without colliding with custom fields added in the interim. Until 4.0 lands, the operator-side controls above are the normative baseline — they do not match the strength of a signed pointer, but they raise the cost of pointer-swap attacks above the cost of a routine CDN compromise, which is what 3.x can promise honestly.
Next steps
- adagents.json Tech Spec — full schema reference, authorization patterns, and validation behavior
- Property Governance overview — how adagents.json fits into the broader governance model
- AdAgents.json Builder — interactive validator and file creator
- @adcp/sdk — TypeScript client library with network consistency checking