> ## 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.

# Publisher domains and the property roster

> How a seller account declares the publisher domains it sells, how Apostra checks each domain's adagents.json, how to read the property roster, and how a verified public listing domain is served.

A publisher domain is a site, app or network domain whose inventory you sell. Apostra checks each one against the
publisher's own `adagents.json`, shows you the verdict, and records the properties the publisher declares under it.
The set of domains you declare, with what Apostra resolves under them, is your **coverage**. The **property roster**
is the read of that coverage.

This page explains how a domain is checked, what the setup checklist and the roster show, and how the public listing
domain is served. To change the set, see [Re-checking a publisher in your coverage](https://docs.apostra.com/v3/seller-publisher-coverage.md).

## Which domain is which

Your brand domain, `operatorDomain`, says who is selling. Your publisher domains say what is being sold. They often
differ. A network sells other companies' sites, and its brand domain is not one of them. A brand domain is never
coverage. [Which domain is which](https://docs.apostra.com/v3/domains.md) explains the third domain, the one your account is registered under, and
when it decides whether your brand domain can be verified.

> [!NOTE]
>
> Only account admins can declare, replace or remove publisher domains, and declare or retract properties. Other
> members can read coverage and re-check a publisher.

## Set your publisher domains

`save_coverage` declares the domains. `add` and `remove` change the domains you name, and `domains` replaces the whole
set. [Re-checking a publisher in your coverage](https://docs.apostra.com/v3/seller-publisher-coverage.md#add-remove-or-replace-what-you-declare)
has the rules for each.

* **Declaring a domain starts its check.** Apostra reads the publisher's `adagents.json` straight away instead of
  waiting for the next scheduled check. Read your coverage again to see the result.
* **Declaring a domain again re-checks it,** unless its last completed check was less than 10 minutes ago. Inside that
  window the call changes nothing: the domain keeps its current status and no new fetch starts. After you fix a
  publisher's file, declare the domain again, or use `refresh`, to get a fresh answer without waiting.
* **A `domains` replace starts checks for at most 20 domains,** the ones never checked or past the 10 minutes. The
  scheduled check covers the rest.
* **`domains: []` declares an empty set** and removes every domain you declared. Domains Apostra crawled or discovered
  for you stay.
* **A seller with no declared domain cannot go live.** The `publisher_domains` step in the
  [setup checklist](https://docs.apostra.com/v3/seller-readiness.md) stays a blocker until at least one domain is declared.

## How a domain's status reads

Each declared domain has a status in your setup checklist and in `search` and `get` with `kind: "coverage"`. Coverage
reads call it `status`.

| Setup label | `status` in a coverage read | What it means |
| - | - | - |
| Checking | `pending` | The first check has not finished. |
| Authorized | `authorized` | The publisher's file authorizes the agent that sells your inventory. |
| Not authorized | `unauthorized` | The file was read and does not authorize that agent. |
| Authorization unknown | `null` | There is no file, an unusable file, or no conclusive evidence either way. |
| Check failed | `error` | The latest check could not finish. Apostra retries on its own. |

Two fields sit under `status`. `adagentsStatus` says whether the file was read, and `authorizationReason` says why a
domain is not authorized.

| `adagentsStatus` | Meaning | What to do |
| - | - | - |
| `resolved` | A usable `adagents.json` was read. | Read `authorizationStatus`. |
| `no_adagents` | Apostra looked and found no file, or the domain does not exist. | Publish a file, or fix the domain you declared. |
| `invalid` | Something is served at the well-known path that is not a usable `adagents.json`. A web page is the usual cause. | Fix that file. Do not publish a second one. |
| `error` | The check could not finish: a timeout, a DNS failure or a server error. | Nothing. Apostra retries. |
| `pending` | Not checked yet. | Wait, or re-check. |

[What each reason means](https://docs.apostra.com/v3/seller-publisher-adagents.md#what-each-reason-means) lists every `authorizationReason` and the
change that fixes it. [Authorizing Apostra in a publisher's adagents.json](https://docs.apostra.com/v3/seller-publisher-adagents.md) gives the
entry the publisher adds.

### When the whole set goes back to Checking

Publishers authorize one agent URL for your whole Storefront, so the check restarts when that URL changes. Apostra
re-checks every domain when the Storefront moves between selling only through sales agents you connected and having
Apostra run a source, and when a Storefront that only uses your own agents changes which agents it uses or their URLs.
Adding a second source Apostra runs does not change the URL, and does not restart anything.

### What blocks go-live and what does not

* A missing declaration is a blocker, as above.
* **Publisher authorization** is advice. Setup shows `publisher_authorization` as partial while any domain is not
  authorized, and it never blocks go-live or a transaction.
* **Product publisher domains** is a warning that counts the products buyers can receive that name no publisher domain
  or name one you have not declared. Draft, archived and other hidden products do not count. Declare the domain, fix
  the domain in the ad server or other source and refresh the catalog, or archive the product.
* A domain that does not exist, or that Apostra could not reach, shows in the `publisher_domains` step as a note.
  The step stays partial and is not a blocker.
* A Storefront that sells through a platform connection, such as Meta or TikTok, has no publisher authorization step.

## What buyers see

Declaring a domain does not change your Storefront's name, operator identity or buyer-facing profile. A buyer reading a
product with `get_products` sees `publisher_properties`, the publisher domains the product covers. A product that
carries real coverage keeps it. A run-of-network product that names only a placeholder domain shows the properties
your Apostra-run sales agent is authorized to sell, so buyers see what they are buying.

## The property roster

The roster is the seller view of coverage: each domain with the properties the publisher declares under it, its
authorization, and what Apostra resolved from the publisher's file. Apostra never invents a property. The roster holds
what the publisher declares and what you declare yourself.

Read it in either of two ways.

* `get({ "kind": "coverage" })` returns the whole roster, and `search({ "kind": "coverage" })` lists and filters it.
* `open_page({ "page": "property_roster" })` opens the Property Roster page, where you add domains, re-check a
  publisher and see each publisher's creative formats.

`search` takes a `query` that matches a domain or any property under it by name, `property_id` or identifier value, and
a `filter` with `authorization` to list only domains with one verdict. A search for a property's name, such as
"Example News" below, finds the domain it sits under.

```json theme={null}
{
  "kind": "coverage",
  "object": {
    "total": 1,
    "verified": 1,
    "partialRoster": false,
    "coverageApplies": true,
    "domains": [
      {
        "domain": "examplenews.com",
        "adagentsStatus": "resolved",
        "status": "authorized",
        "authorizationStatus": "authorized",
        "authorizationReason": "authorized",
        "provenance": "declared",
        "propertyCount": 1,
        "properties": [
          {
            "property_key": "examplenews_web",
            "property_id": "examplenews_web",
            "property_type": "website",
            "name": "Example News",
            "identifiers": [{ "type": "domain", "value": "examplenews.com" }],
            "tags": [],
            "provenance": "publisher_adagents",
            "authorization": {
              "status": "authorized",
              "grain": "domain",
              "reason": "authorized"
            }
          }
        ]
      }
    ]
  }
}
```

`total` is how many domains you have declared, and `verified` is how many of them authorize you. `provenance` on a
domain is `declared`, `crawled` or `discovered`.

### A partial read is not a missing domain

A read of a large network does not always carry every property. `partialRoster: true` says it left rows out. Then an
absence is not evidence that a site is missing, and a search that matched nothing is not evidence that you do not sell
it. Read `propertyCount`, the number of properties the domain actually has, and not the length of `properties`. Search
for the specific domain or property, or open the Property Roster page, before you decide a site is not covered.

### Whether one property is authorized

Each property carries its own `authorization`. Read `grain` before you act on `status`. A publisher authorizes agents in
its domain's `adagents.json`, and that is where Apostra resolves the verdict, so `grain` is `domain`.

* **`unauthorized` is definite.** The domain does not authorize your agent, so nothing under it can be sold, however many
  properties the file declares.
* **`authorized` is inherited from the domain.** A publisher can map an agent to only some of the properties it
  declares. Apostra does not prove a positive against the selector for that one property.
* **`unknown` means no publisher declaration backs the property.** It is usually a property you declared yourself. It
  stays on your roster and cannot read as authorized until the publisher's file declares it.

`authorization.reason` is the reason on the domain, so the fix travels with the property.

### Storefronts where authorization does not apply

`coverageApplies` is `false` for a Storefront that sells through a platform connection such as Meta, Google or TikTok.
Its selling rights come from that connection, no `adagents.json` will resolve for it, and every domain reads as not
authorized. That is not a problem to fix. Do not publish a file to change it.

### What changes when a publisher's file changes

Apostra replaces a domain's publisher-declared properties with the latest file. A property the new file no longer
declares leaves the live roster. A publisher whose `adagents.json` is gone loses its declared properties from the roster
until the file returns, and a file that leaves out the `properties` key changes nothing. A property you declared
yourself stays through all of these, shown as your declaration and visibly not backed by the publisher. If the file
declares the same property later, the publisher's record takes over its identity and your claim stays recorded on it.

### Declare a property before the publisher does

`declareProperties` on `save_coverage` records a property you sell under a domain you already declared. Give a
`property_id`, an identifier or a name, because the roster never invents an identity. It carries
`provenance: "seller_declared"` and stays distinct from publisher-declared properties.
[Re-checking a publisher in your coverage](https://docs.apostra.com/v3/seller-publisher-coverage.md#add-remove-or-replace-what-you-declare) has the
rules for it and for retracting a claim.

### Creative formats on the roster

The Property Roster page also shows the creative formats each publisher declares. They are not in the `coverage`
reads. A format is a canonical `format_kind` with its `params`, with no agent URL, and each one keeps its provenance
and the roster properties it applies to.

* Formats come from the AgenticAdvertising.org (AAO) registry's publisher lookup, which also holds registry
  declarations for platforms that publish no file of their own. When that lookup is unavailable, a `formats[]` the
  publisher's live file declares keeps the catalog current. A catalog the registry returns holds precedence once it is
  available again. A registry declaration never authorizes your sales agent.
* Each format shows its provenance, `publisher_adagents` for the publisher's own declaration or `aao_registry`.
* A declaration with no property scope applies to every property under the publisher. A scoped one applies to a
  property when its `property_id` is listed or one of its tags overlaps.
* The format catalog has its own freshness, `formatCatalogSyncedAt`. It is separate from the file check's
  `lastSyncedAt`. When neither lookup gives a complete catalog, the roster keeps the last good one and does not treat
  the failure as an empty catalog.
* On the page, **Create product** opens the product task with that format already selected. Choose the ad server and
  the exact inventory, choose the delivery formats that source reports, then **Review product** and **Create draft
  product**. A format the publisher gave no stable ID shows **Format needs an ID** and cannot be attached yet.
* **Review property mapping** means the full publisher roster was checked and no property matches the format.
  **Check inventory mapping** means the roster view did not carry enough properties to prove either way, so use it
  instead of treating the format as unavailable.

## Public listing domain

A public listing domain is the verified hostname where your approved public listing is served. It is separate from your
publisher domains. Set it up in **Public distribution** on the Public profile page, which
`open_page({ "page": "listing" })` opens.
[Choose a domain for Public distribution](https://docs.apostra.com/v3/domains.md#choose-a-domain-for-public-distribution) has the DNS records and
the check.

The page has four steps: **Identity**, **What you do**, **Listed on Apostra** and **Public distribution**. A Storefront
that has not published shows **Not listed yet**. Turning **Listed on Apostra** off, after a confirmation, takes the
Storefront off Apostra and the public web until you publish again. Public distribution needs the Distribution add-on and
a verified domain, and verifying a domain needs the listing to be published on Apostra first. A verified domain does not
publish anything: choose **Make it public** to take Public distribution live. A Demo Storefront counts only the first
two steps toward being Demo ready.

### How the domain is served

* **A hostname serves the last revision that passed its checks.** When you publish a replacement revision, the verified
  one stays live until the replacement passes.
* **Anything unverified is not served.** A draft, a private listing, a suspended or expired registration, and a
  revision that was never verified all fail closed. Apostra returns `404` for them and `410` for a revoked listing.
* **Apostra rechecks the hostname** on a schedule: DNS, HTTPS, ownership and the publication's identity. Serving stops
  when verification expires or the listing stops being public.

| Route | Content |
| - | - |
| `https://{hostname}/` | The listing as a page |
| `https://{hostname}/card.json` | The listing as JSON |
| `https://{hostname}/embed` | An embeddable version of the page |

Each hostname also answers `/.well-known/interchange-discovery-registration` with a routing proof.

### OpenAI Apps domain verification

The ChatGPT app destination needs Public distribution live first. Its domain-ownership step stores the challenge token
OpenAI gives you on your hostname registration. Apostra then serves it as plain text, with caching off, at:

```text theme={null}
https://{hostname}/.well-known/openai-apps-challenge
```

OpenAI fixes that address, so it is not nested under any listing path. Save the token with `save_seller` and its
`distribution` input:

```json theme={null}
{
  "distribution": {
    "openaiChallengeToken": "openai-token-from-the-submission-form",
    "confirmReplace": true
  }
}
```

Replacing a token needs `confirmReplace: true`, and removing one, with `openaiChallengeToken: null`, needs
`confirmRemove: true`. A replacement invalidates the previous value at once, and Apostra records publication,
replacement and removal events in an audit history. `get({ "kind": "distribution" })` returns whether a token is set and
the status of its latest check, never the token. The check asks whether your hostname returns the exact value. It does
not call OpenAI and does not claim that OpenAI has verified the domain.

## Related

* [Re-checking a publisher in your coverage](https://docs.apostra.com/v3/seller-publisher-coverage.md) declares, removes and re-checks domains.
* [Authorizing Apostra in a publisher's adagents.json](https://docs.apostra.com/v3/seller-publisher-adagents.md) gives the entry a publisher adds.
* [Which domain is which](https://docs.apostra.com/v3/domains.md) separates your registered, brand and publisher domains.
* [Seller readiness](https://docs.apostra.com/v3/seller-readiness.md) reads the setup checklist and the go-live verdict.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.