Skip to content
4 changes: 4 additions & 0 deletions .changeset/geo-metro-registry.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
---
---

Empty changeset: Adds the AAO geo metro reference registry — a JSON schema for the registry entry shape, an initial verified `nielsen_dma` seed (~27 markets), and reference documentation covering agent usage for name-to-code resolution and code-to-name display.
1 change: 1 addition & 0 deletions docs.json
Original file line number Diff line number Diff line change
Expand Up @@ -1787,6 +1787,7 @@
]
},
"docs/reference/gmsf-reference",
"docs/reference/geo-metro-registry",
"docs/reference/media-channel-taxonomy",
"docs/measurement/taxonomy",
"docs/reference/roadmap",
Expand Down
143 changes: 143 additions & 0 deletions docs/reference/geo-metro-registry.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
---
title: Geo Metro Registry
description: "AAO-maintained reference tables mapping numeric geo metro codes to canonical names and aliases for use in AdCP targeting."
"og:title": "AdCP Geo Metro Registry"
---

# Geo Metro Registry

AdCP's `geo_metros` targeting field accepts system-qualified numeric codes:

```json
{
"geo_metros": [
{ "system": "nielsen_dma", "values": ["501", "532"] }
]
}
```

Buyers think in market names, not codes. Agents that guess codes from training data produce hallucinations — "Albany" matches both DMA 525 (Albany, GA) and DMA 532 (Albany-Schenectady-Troy, NY), and resolving to the wrong one silently misdirects spend. The AAO geo metro registry provides authoritative code-to-name tables so agents can resolve names correctly and display human-readable labels for stored codes.

## Registry Structure

One registry file per system. Registry data files are available in the AdCP repository under `static/schemas/source/registries/`.

<Note>
A `GET /api/reference/geo-metros/{system}` endpoint is planned to serve registry data over HTTP. Until it ships, consume registry files directly from the repository or bundle them in your SDK.
</Note>

Each registry file conforms to the [geo-metro-registry schema](https://adcontextprotocol.org/schemas/v3/registries/geo-metro-registry.json) and includes:

| Field | Description |
|-------|-------------|
| `system` | The metro system identifier (matches `metro-system.json` enum) |
| `version` | Semver — bump on every published change |
| `digest` | sha256 of the `entries` array (sorted keys, hex-encoded) for cache validation |
| `last_updated` | ISO date of the last published change |
| `entries[]` | All code-name pairs for this system |

Each entry:

| Field | Description |
|-------|-------------|
| `code` | Opaque numeric string — the value used in `geo_metros.values[]` |
| `name` | Official canonical market name |
| `aliases[]` | Common variants: abbreviations, historical names, city-only forms |
| `region` | ISO 3166-2 region code (informational, where applicable) |
| `country` | ISO 3166-1 alpha-2 country code (required for non-US systems) |
| `deprecated` | `true` if the system authority has discontinued this market |
| `deprecated_reason` | Human-readable explanation of deprecation |

## Supported Systems

| System | Status | Coverage | Source |
|--------|--------|----------|--------|
| `nielsen_dma` | Available | ~27 verified US markets (initial seed; expanding to ~210) | Nielsen DMA regions |
| `uk_itl1` | Planned | 12 UK ITL Level 1 regions | ONS ITL reference |
| `uk_itl2` | Planned | ~41 UK ITL Level 2 regions | ONS ITL reference |
| `eurostat_nuts2` | Planned | ~240 EU NUTS Level 2 regions | Eurostat NUTS reference |

`custom` system values are provider-scoped and are not covered by the AAO registry.

## Agent Usage

### Name → code (write direction)

When a buyer provides a market name, resolve it before setting `geo_metros`:

1. Load the registry for the relevant system.
2. Match the buyer's input against `name` and `aliases[]` (case-insensitive, trimmed).
3. If multiple entries match, surface all matches and ask the buyer to confirm. For example, "Albany" matches both DMA 525 (Albany, GA) and DMA 532 (Albany-Schenectady-Troy, NY) — these are distinct markets in different states.
4. Use the confirmed `code` in the `values[]` array.

**Never infer a code from training data.** DMA codes are a fixed, non-sequential numbering scheme — market size rank does not correspond to code. An agent guessing codes silently misdirects spend.

### Code → name (read direction)

When displaying campaign targeting to a user, resolve stored codes to names:

1. Load the registry for the system.
2. Look up each code in `entries[]` by `code`.
3. Display `name`. If a code is not found, display the raw code — it may be a deprecated market or one not yet in the registry seed.

### Caching

Cache registry data by `version`. Use the `digest` field (sha256 of `entries` with sorted keys, hex-encoded) to detect stale caches without comparing entries entry-by-entry. A 24-hour TTL is appropriate — registries change infrequently.

## Example: Nielsen DMA Lookup

**Abbreviated `nielsen_dma` data file:**
```json
{
"system": "nielsen_dma",
"version": "1.0.0",
"entries": [
{
"code": "501",
"name": "New York, NY",
"aliases": ["NYC", "New York City", "New York"]
},
{
"code": "525",
"name": "Albany, GA",
"aliases": ["Albany"]
},
{
"code": "532",
"name": "Albany-Schenectady-Troy, NY",
"aliases": ["Albany", "Albany NY", "Schenectady", "Troy NY", "Capital Region"]
},
{
"code": "602",
"name": "Chicago, IL",
"aliases": ["Chicago"]
},
{
"code": "803",
"name": "Los Angeles, CA",
"aliases": ["LA", "Los Angeles", "L.A."]
}
]
}
```

A buyer saying "target Albany" triggers an ambiguity — both DMA 525 and DMA 532 carry "Albany" in their aliases. The agent surfaces both and asks which market the buyer intends before writing `geo_metros`.

## Governance

Registry entries are additive. Once published, a code is never removed — it may be marked `deprecated: true` with a `deprecated_reason` if the system authority discontinues a market. Name changes follow the source system's official release; the previous name moves to `aliases[]`.

The `digest` field is sha256 of the `entries` array serialized with sorted keys. It is computed at publish time and changes whenever any entry changes; clients use it for cache invalidation.

Corrections and additions land via PR against [adcontextprotocol/adcp](https://github.com/adcontextprotocol/adcp) with a reference to the source system's authoritative publication.

## Long-term Direction

The geo metro registry is the first of a broader class of shared reference registries that AAO maintains on behalf of the AdCP ecosystem — covering reference data that is:

- Standardized or semi-standardized across many sellers
- Needed for natural-language resolution by agents
- Stable enough to version
- Painful and duplicative for every provider to ship independently

The `nielsen_dma` file ships as a verified seed (~27 markets). The full ~210 US DMA list and the ITL and NUTS registries for UK and EU markets are tracked as follow-up contributions. Property identity registries (mobile apps, CTV apps, domains) are tracked separately under the property registry design.
40 changes: 40 additions & 0 deletions static/schemas/source/registries/geo-metro-nielsen_dma.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"$id": "/schemas/registries/geo-metro-nielsen_dma.json",
"title": "Nielsen DMA Geo Metro Registry",
"description": "AAO-maintained registry of Nielsen Designated Market Area (DMA) codes and canonical market names for use in AdCP geo_metros targeting. Initial verified seed — expanding to full ~210 US markets.",
"system": "nielsen_dma",
"version": "1.0.0",
"last_updated": "2026-06-18",
"digest": "2e2fb3ca2059e0a6d173b036dbf11caa310e219e547e769c73b48b7be2dd3334",
"source_url": "https://www.nielsen.com/dma-regions/",
"entries": [
{ "code": "501", "name": "New York, NY", "aliases": ["NYC", "New York City", "New York"] },
{ "code": "504", "name": "Philadelphia, PA", "aliases": ["Philadelphia", "Philly"] },
{ "code": "505", "name": "Detroit, MI", "aliases": ["Detroit"] },
{ "code": "506", "name": "Boston, MA (Manchester)", "aliases": ["Boston", "Manchester NH"] },
{ "code": "508", "name": "Pittsburgh, PA", "aliases": ["Pittsburgh"] },
{ "code": "510", "name": "Cleveland-Akron (Canton), OH", "aliases": ["Cleveland", "Akron", "Canton"] },
{ "code": "511", "name": "Washington, DC (Hagerstown)", "aliases": ["Washington DC", "DC", "Washington", "Hagerstown"] },
{ "code": "524", "name": "Atlanta, GA", "aliases": ["Atlanta"] },
{ "code": "525", "name": "Albany, GA", "aliases": ["Albany"] },
{ "code": "531", "name": "Tri-Cities, TN/VA", "aliases": ["Tri-Cities", "Johnson City", "Kingsport", "Bristol"] },
{ "code": "532", "name": "Albany-Schenectady-Troy, NY", "aliases": ["Albany", "Albany NY", "Schenectady", "Troy NY", "Capital Region"] },
{ "code": "543", "name": "Springfield-Holyoke, MA", "aliases": ["Springfield MA", "Holyoke"] },
{ "code": "602", "name": "Chicago, IL", "aliases": ["Chicago"] },
{ "code": "610", "name": "Rockford, IL", "aliases": ["Rockford"] },
{ "code": "618", "name": "Houston, TX", "aliases": ["Houston"] },
{ "code": "623", "name": "Dallas-Ft. Worth, TX", "aliases": ["Dallas", "Fort Worth", "DFW"] },
{ "code": "628", "name": "Monroe, LA-El Dorado, AR", "aliases": ["Monroe LA", "El Dorado AR"] },
{ "code": "634", "name": "Amarillo, TX", "aliases": ["Amarillo"] },
{ "code": "709", "name": "Tyler-Longview (Lufkin & Nacogdoches), TX", "aliases": ["Tyler TX", "Longview TX", "Lufkin", "Nacogdoches"] },
{ "code": "722", "name": "Lincoln-Hastings-Kearney, NE", "aliases": ["Lincoln NE", "Hastings NE", "Kearney NE"] },
{ "code": "734", "name": "Jonesboro, AR", "aliases": ["Jonesboro"] },
{ "code": "743", "name": "Anchorage, AK", "aliases": ["Anchorage"] },
{ "code": "753", "name": "Phoenix (Prescott), AZ", "aliases": ["Phoenix", "Prescott AZ"] },
{ "code": "798", "name": "Glendive, MT", "aliases": ["Glendive"] },
{ "code": "803", "name": "Los Angeles, CA", "aliases": ["LA", "Los Angeles", "L.A."] },
{ "code": "807", "name": "San Francisco-Oakland-San Jose, CA", "aliases": ["San Francisco", "SF", "Bay Area", "Oakland", "San Jose"] },
{ "code": "819", "name": "Seattle-Tacoma, WA", "aliases": ["Seattle", "Tacoma"] }
]
}
78 changes: 78 additions & 0 deletions static/schemas/source/registries/geo-metro-registry.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"$id": "/schemas/registries/geo-metro-registry.json",
"title": "Geo Metro Code Registry",
"description": "AAO-maintained registry mapping numeric geo metro codes to canonical names and aliases for each supported metro classification system.\n\n**Purpose.** AdCP's `geo_metros` targeting field accepts system-qualified numeric codes (e.g. `{ \"system\": \"nielsen_dma\", \"values\": [\"532\"] }`). Human buyers know markets by name, not code. This registry provides the authoritative code → name mapping and alias table so agents can resolve natural-language market names to the correct codes without hallucination, and display human-readable labels for stored codes.\n\n**Systems covered.** One registry file per system, named `geo-metro-{system}.json` (e.g. `geo-metro-nielsen_dma.json`). Each file conforms to this schema. The set of valid system identifiers is defined by `/schemas/enums/metro-system.json`.\n\n**Agent usage (normative).**\n1. *Name → code (write direction):* When a buyer provides a market name (e.g. \"Albany\" or \"Albany, NY\"), the agent MUST fetch the registry for the relevant system and match against `name` and `aliases[]` before setting `geo_metros`. The agent MUST NOT guess or infer a code from training data.\n2. *Code → name (read direction):* When displaying campaign targeting to a user, the agent SHOULD resolve stored codes to names via the registry and present `name`, not raw codes.\n3. *Ambiguous input:* When an input matches multiple entries (e.g. \"Albany\" matches Albany, GA (DMA 525) and Albany-Schenectady-Troy, NY (DMA 532)), the agent MUST surface all matches and ask the buyer to confirm.\n\n**Caching.** Clients SHOULD cache registry files by `version`. The `digest` field (sha256 of the `entries` array serialised with sorted keys, hex-encoded) lets clients detect changes without re-fetching. Registries are updated infrequently; a TTL of 24 hours is appropriate.\n\n**Governance.** Entries are additive. Once published, a code entry is never removed (it may be marked `deprecated: true` with a `deprecated_reason` if a market is discontinued or renamed). Name changes follow the source system's official release; the previous name moves to `aliases[]` and the official current name becomes `name`. Additions and corrections land via PR with a reference to the source system's authoritative publication.",
"type": "object",
"required": ["system", "version", "last_updated", "digest", "entries"],
"properties": {
"system": {
"$ref": "/schemas/enums/metro-system.json",
"description": "The metro classification system these entries belong to. Matches the `system` field used in AdCP `geo_metros` targeting."
},
"version": {
"type": "string",
"description": "Semver of this registry file. Bumped on every published change."
},
"last_updated": {
"type": "string",
"format": "date",
"description": "ISO date of the last published change."
},
"digest": {
"type": "string",
"description": "sha256 of the `entries` array serialised with sorted keys, hex-encoded. Clients use this to detect stale caches without comparing entry-by-entry."
},
"source_url": {
"type": "string",
"format": "uri",
"description": "URL of the authoritative source publication for this registry (e.g. Nielsen DMA documentation, Eurostat NUTS reference)."
},
"entries": {
"type": "array",
"description": "All known code-name pairs for this system. Ordered by code ascending.",
"items": {
"$ref": "#/definitions/GeoMetroEntry"
}
}
},
"definitions": {
"GeoMetroEntry": {
"type": "object",
"required": ["code", "name"],
"properties": {
"code": {
"type": "string",
"description": "The numeric string code used in AdCP `geo_metros.values[]`. Must be treated as an opaque string identifier — do not cast to integer."
},
"name": {
"type": "string",
"description": "Official canonical name for this market as published by the system authority."
},
"aliases": {
"type": "array",
"items": { "type": "string" },
"description": "Alternative names buyers commonly use to refer to this market, including historical names, abbreviations, and city-only variants. Used for fuzzy name resolution."
},
"region": {
"type": "string",
"description": "ISO 3166-2 region code for the primary region this market falls within (where applicable). Informational only."
},
"country": {
"type": "string",
"description": "ISO 3166-1 alpha-2 country code. Required for non-US systems."
},
"deprecated": {
"type": "boolean",
"default": false,
"description": "When true, this market has been discontinued by the system authority. The code is retained for backward-compatibility with stored campaigns but SHOULD NOT be offered in new targeting."
},
"deprecated_reason": {
"type": "string",
"description": "Human-readable explanation of why this entry is deprecated (e.g. 'Market discontinued in Nielsen 2024 DMA refresh')."
}
},
"additionalProperties": false
}
}
}
Loading