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

# adagents.json for network sellers

> How to publish publisher properties, manager-domain delegation, and authorized agents for a storefront

## Overview

`adagents.json` is the publisher-owned file that tells Apostra two things:

* which properties exist, through `properties[]`
* which agents are authorized to sell them, through `authorized_agents[]`

Publish it at:

```text theme={null}
https://<publisher-domain>/.well-known/adagents.json
```

For a network, the file often lives on a manager domain and covers many managed
publisher domains. The publishers delegate to that manager through `ads.txt`
`MANAGERDOMAIN=<manager-domain>` or an `authoritative_location` pointer.

## Schema pointer

Include the schema URL in the file so validators and crawlers can lint the same
shape:

```json theme={null}
{
  "$schema": "https://adcontextprotocol.org/schemas/v3/adagents.json"
}
```

## Minimal network file

This example declares two properties and authorizes Apostra storefront
agent for properties tagged `food`.

```json theme={null}
{
  "$schema": "https://adcontextprotocol.org/schemas/v3/adagents.json",
  "properties": [
    {
      "property_id": "wholesome_yum",
      "property_type": "website",
      "name": "Wholesome Yum",
      "identifiers": [{ "type": "domain", "value": "wholesomeyum.com" }],
      "tags": ["food"]
    },
    {
      "property_id": "tastes_better_from_scratch",
      "property_type": "website",
      "name": "Tastes Better From Scratch",
      "identifiers": [
        { "type": "domain", "value": "tastesbetterfromscratch.com" }
      ],
      "tags": ["food"]
    }
  ],
  "authorized_agents": [
    {
      "url": "https://interchange.io",
      "authorized_for": "Example Food Network",
      "authorization_type": "property_tags",
      "property_tags": ["food"],
      "delegation_type": "ad_network"
    }
  ]
}
```

## `properties[]`

Each `properties[]` entry should describe one site, app, or CTV app.

| Field           | What to provide                                                                              |
| --------------- | -------------------------------------------------------------------------------------------- |
| `property_id`   | A stable, lowercase identifier for the property. Do not reuse it for a different site later. |
| `property_type` | The property type, such as `website` for a web property.                                     |
| `name`          | The human-readable property name.                                                            |
| `identifiers[]` | Typed identifiers. For web, include `{ "type": "domain", "value": "<domain>" }`.             |
| `tags[]`        | Optional stable group labels used by authorization or internal organization.                 |

Tags can help authorize a set of properties without listing every property id
in the agent entry. Keep tags operationally stable: changing a tag can change
which properties the agent is authorized to sell.

## `authorized_agents[]`

Each `authorized_agents[]` entry names one agent and the scope it may sell.
Apostra does not treat the agent's mere presence in the array as
authorization. The entry should say which properties the agent is authorized
for.

The entry's `url` is the agent identity and executable discovery base. The URL
where this JSON document is hosted is separate: never use the publisher's
well-known URL or the resolved `authoritative_location` as a connected
inventory source's `endpointUrl`.

Common selector styles:

| Selector                                                                   | Use when                                                                                                       |
| -------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------- |
| `authorization_type: "property_ids"` with `property_ids[]`                 | You want to authorize a fixed set of property ids.                                                             |
| `authorization_type: "property_tags"` with `property_tags[]`               | You want to authorize every property carrying one or more tags.                                                |
| `authorization_type: "publisher_properties"` with `publisher_properties[]` | You need a cross-publisher selector, such as `{ "publisher_domain": "example.com", "selection_type": "all" }`. |

For network-scale authorization today, use
`authorization_type: "publisher_properties"` with `publisher_properties[]` when
you need a cross-publisher selector. Product responses still disclose coverage
through `publisher_properties[]`, usually one selector per publisher domain.

## Review and generation checklist

Before publishing a new or corrected file, check the complete document rather
than only the field that prompted the edit:

* Emit plain JSON. JSON string values must use literal quote characters, not
  HTML entities such as `&quot;`; URL values must not contain chat-link markup such
  as `<https://example.com|example.com>`.
* Keep every `property_id` lowercase, using only letters, numbers, and
  underscores. Every value selected by `property_ids[]` must exactly match a
  top-level `properties[].property_id` in the same resolved document. If an
  existing draft uses uppercase letters, normalize the declaration and every
  reference to it consistently before publishing. First check whether
  lowercasing would collide with another property ID in the document. If it
  would, do not guess which property should keep the normalized ID; choose new
  distinct lowercase IDs with the operator and update their references.
* Pair `authorization_type` with its matching non-empty selector. For example,
  `authorization_type: "property_ids"` requires `property_ids[]`.
* Choose `delegation_type` from the commercial relationship, not from who hosts
  the file or operates the software: `direct` means the publisher treats the
  agent as a direct buying path, `delegated` means the agent sells on the
  publisher's behalf, and `ad_network` means network or package distribution.
* Set `last_updated` to the ISO 8601 time of the actual change. Do not carry an
  older timestamp into a corrected file.
* When producing several files, make each one a complete standalone JSON
  document and validate each document separately against the schema URL in its
  `$schema` field.

Parsing JSON and checking references are useful review steps, but they are not
the same as schema validation. Only describe a file as **schema-validated** when
a schema validator has actually accepted that exact final payload. Otherwise,
state which structural and semantic checks you performed and recommend schema
validation before publication.

### Which agent URL to use

The `url` field names the agent being authorized. Which URL to use depends on how
the seller connects to Apostra:

| Seller setup                                                                    | Agent URL to authorize                                    |
| ------------------------------------------------------------------------------- | --------------------------------------------------------- |
| Storefront run by experts, including an ad server connected directly to Apostra | `https://interchange.io`                                  |
| Storefront that sells only through connected external AdCP agents               | The external agent's own published URL                    |
| Storefront with both an external agent and inventory run by Apostra             | `https://interchange.io`                                  |
| Upstream storefront linked as an inventory source                               | The upstream storefront's own authorization model applies |

For storefronts run by experts, the canonical URL is always
`https://interchange.io`. Per-customer subdomain URLs (for example
`https://yourname.example.apostra.com`) are not the correct entry; use the
canonical URL. The buyer-facing execution endpoint is
`https://<seller-cname>/adcp/mcp` when a verified publication CNAME exists, or
`https://api.apostra.com/seller/{platformId}/mcp` on the shared host. It is
how buyers call a specific Seller; it is not that Seller's publisher-authorization
identity and must not be placed in `authorized_agents[].url`. See
[Authentication](/v2/authentication) for the full base URL reference.

For an external-only storefront with more than one connected agent, a publisher
is authorized when its file grants property authority to at least one of those
agents. Different publishers can authorize different connected agents. If the
storefront also includes inventory run by Apostra, the storefront-level
authorization status uses `https://interchange.io` so an unrelated external
agent cannot authorize that inventory.

### Authorization is advisory, not a blocker

Apostra surfaces a missing or mismatched `adagents.json` authorization as a
**warning** during setup and product authoring. It does not block source
connection, product authoring, or activation. However, when coverage disclosure
is enabled, authorized properties from this file are the source for buyer-visible
coverage; missing authorization reduces what buyers see, even if the storefront
itself stays active. See
[Identity documents](/v2/concepts/identity-documents) for the full explanation of
what blocks setup versus what is advisory.

## Manager-domain delegation

If every managed publisher hosted its own full file, a network would have to
coordinate thousands of files. Instead, a publisher can delegate to the manager
domain.

For an `ads.txt` delegation, the publisher adds:

```text theme={null}
MANAGERDOMAIN=cafemedia.com
```

Apostra resolves the publisher domain, sees the manager-domain delegation,
and reads:

```text theme={null}
https://cafemedia.com/.well-known/adagents.json
```

The manager file then supplies the properties and authorized agents for the
managed footprint.

## `authoritative_location` stubs

A publisher can also host a small pointer file that delegates one hop to a
centrally hosted document:

```json theme={null}
{
  "authoritative_location": "https://cafemedia.com/.well-known/adagents.json"
}
```

Use this when a publisher can host a file at its own well-known URL but wants
the full document maintained centrally. The publisher-origin URL must remain
reachable so crawlers can verify that the pointer is the publisher's act.

## How Apostra ingests it

Apostra resolves `adagents.json` through the same identity-document path
used during storefront setup:

1. Read the publisher's own well-known file when present.
2. Follow a valid one-hop `authoritative_location` pointer when the publisher
   file is a stub.
3. Fall back to the publisher's `ads.txt` `MANAGERDOMAIN` when no direct file is
   present.
4. Hand the resolved file to the managed sales agent, which classifies the
   properties and authorized agents.
5. Use the resulting authorized properties as the source for buyer-visible
   coverage when coverage disclosure is enabled for the managed storefront.

Apostra references publisher authorization; it does not mint or edit
`authorized_agents[]` for a publisher. Granting authorization must originate in
the publisher-controlled document or delegation.

### SDK 14 response compatibility

The SDK 14 storefront migration does not change the `adagents.json` document
shape or its authorization rules. It does change the AdCP responses buyers see
after an authorized inventory source is connected:

* Storefront metadata is canonical under `ext.scope3.storefront`. Capability
  responses temporarily also emit the deprecated
  `extensions.scope3.storefront` alias for AdCP 3.0 and 3.1 clients.
* AdCP 3.1 products publish canonical `format_options[]`; buyers should retain
  the selected `format_option_id` and its format reference when creating media
  buys or syncing creatives. When a buyer negotiates AdCP 3.0, products instead
  carry the required projected `format_ids[]` legacy references.
* `sync_creatives` requires `format_id`. Media-buy task and webhook routing uses
  the hyphenated `media-buy` protocol value.

See [AdCP Storefront responses on SDK 14](/v2/setup/migration-guide#adcp-storefront-responses-on-sdk-14)
for the complete field mapping and migration checklist.

## Common pitfalls

* **Test rows become coverage.** A test property in `properties[]` can be
  disclosed to buyers because it looks like real authorization.
* **Bare agent entries fail closed.** List the authorization scope, not only the
  agent URL.
* **Per-customer subdomain URLs are not the canonical agent URL.** For
  Apostra-connected sellers, authorize `https://interchange.io`, not a
  per-customer subdomain. Using a subdomain creates a valid-looking entry that
  points at the wrong agent.
* **Publisher domains must be real inventory.** Manager and operator domains are
  not substitutes for the site domains being sold.
* **Keep `property_id` stable.** Buyers and agents can use property ids as
  durable selectors.
* **Keep tags clean.** If an authorization uses `property_tags`, tag hygiene
  controls the agent's authorized footprint.
* **Validate `www` and apex domains.** Put the domain buyers should recognize in
  `identifiers[]`; avoid mixed or stale variants.

## Related

<CardGroup cols={2}>
  <Card title="Publisher properties and coverage" href="/v2/storefront/inventory-sources/publisher-properties-coverage" icon="globe">
    How authorized properties appear to buyers in `get_products`.
  </Card>

  <Card title="Identity documents" href="/v2/concepts/identity-documents" icon="id-card">
    The full model for adagents.json, brand.json, agent cards, and AAO.
  </Card>

  <Card title="Discover agents" href="/v2/storefront/tasks/discover-agents" icon="magnifying-glass">
    Resolve a domain's registered and authorized agents.
  </Card>

  <Card title="Custom targeting and properties" href="/v2/storefront/esa/custom-targeting-properties" icon="sliders">
    How GAM key-values relate to site groups and signals.
  </Card>
</CardGroup>
