Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions .changeset/registry-p0-followups-type-guard-docs-ia.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
---
---

Registry P0 follow-ups from #3495: fix crawler type reclassification guard so agents with
wrong non-unknown types can be corrected on future crawl passes; rename OpenAPI tag from
"Lookups & Authorization" to "Authorization Lookups" to fix the %26-encoded URL slug; add
trust-level descriptions to source/member/discovered_from schema fields; document auth-aware
visibility on /operator; add missing operator/publisher rows to docs/registry/index.mdx
overview table; add docs/registry/registering-an-agent.mdx explaining the two registration
paths and trust levels.
6 changes: 4 additions & 2 deletions docs.json
Original file line number Diff line number Diff line change
Expand Up @@ -505,7 +505,8 @@
"directory": "docs/registry/api-reference"
},
"pages": [
"docs/registry/index"
"docs/registry/index",
"docs/registry/registering-an-agent"
]
},
"docs/reference/gmsf-reference",
Expand Down Expand Up @@ -997,7 +998,8 @@
"directory": "docs/registry/api-reference"
},
"pages": [
"docs/registry/index"
"docs/registry/index",
"docs/registry/registering-an-agent"
]
},
"docs/reference/gmsf-reference",
Expand Down
20 changes: 18 additions & 2 deletions docs/registry/index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -84,9 +84,12 @@ Rate-limited endpoints return `429 Too Many Requests` when the limit is exceeded
<Card title="Change Feed" icon="clock-rotate-left" href="/docs/registry/index#change-feed">
Poll a cursor-based feed of registry changes for local sync.
</Card>
<Card title="Lookups & Authorization" icon="shield-check" href="/docs/registry/index#lookups--authorization">
<Card title="Authorization Lookups" icon="shield-check" href="/docs/registry/index#authorization-lookups">
Look up agents by domain, validate product authorization, and check property authorization in real time.
</Card>
<Card title="Registering an Agent" icon="circle-plus" href="/docs/registry/registering-an-agent">
How agents appear in the registry, what trust level each path produces, and what membership unlocks.
</Card>
</CardGroup>

### Brand Resolution
Expand Down Expand Up @@ -139,17 +142,30 @@ All sources produce the same resolution response structure. To get full brand id
| GET | `/api/registry/feed` | Poll cursor-based registry change feed (auth required) |


### Lookups & Authorization
### Authorization Lookups

| Method | Path | Description |
|--------|------|-------------|
| GET | `/api/registry/operator` | What agents does this domain operate? (auth-aware — see [operator lookup](#operator-and-publisher-lookup)) |
| GET | `/api/registry/publisher` | What does this domain publish and which agents does it authorize? |
| GET | `/api/registry/lookup/domain/{domain}` | Find agents authorized for a domain |
| GET | `/api/registry/lookup/property` | Find agents by property identifier |
| GET | `/api/registry/lookup/agent/{agentUrl}/domains` | Get all domains for an agent |
| POST | `/api/registry/validate/product-authorization` | Validate agent product authorization |
| POST | `/api/registry/expand/product-identifiers` | Expand property selectors to identifiers |
| GET | `/api/registry/validate/property-authorization` | Real-time authorization check |

#### Operator and publisher lookup

`/api/registry/operator` and `/api/registry/publisher` answer different questions about the same domain:

| Endpoint | Question | Typical caller |
|----------|----------|----------------|
| `/api/registry/operator?domain=X` | What agents does this entity operate? Which publishers trust it? | Buy-side: validating a sell-side counterparty |
| `/api/registry/publisher?domain=X` | What inventory does this entity publish? Which agents does it authorize? | Sell-side: inspecting a publisher's authorization set |

The operator endpoint returns more data for authenticated callers: anonymous callers see public agents only; callers with an AgenticAdvertising.org API-access token additionally see `members_only` agents; callers who are the profile owner additionally see `private` agents. [See registering an agent](/docs/registry/registering-an-agent) for how trust levels map to registration source.

Authorization validation checks both sides: the publisher's `adagents.json` (does it authorize this agent with the claimed `delegation_type`?) and the operator's `brand.json` (does it declare this property with a matching `relationship`?).

### Validation Tools
Expand Down
57 changes: 57 additions & 0 deletions docs/registry/registering-an-agent.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
---
title: Registering an Agent
sidebarTitle: Registering an Agent
description: "How agents appear in the AgenticAdvertising.org registry, what trust level each path produces, and what AgenticAdvertising.org membership unlocks."
"og:title": "AdCP — Registering an Agent"
---

Agents appear in the AgenticAdvertising.org registry through two paths. The path determines the agent's `source` value in API responses and the trust level assigned to it.

## Registration paths

| Path | How | `source` in API | Trust level |
|------|-----|-----------------|-------------|
| **Member direct registration** | An AgenticAdvertising.org member organization enrolls the agent via the member dashboard. The member has accepted AgenticAdvertising.org terms on the agent's behalf. | `registered` | Full — appears in `public`, `members_only`, and `private` tiers depending on visibility settings |
| **Discovery via adagents.json** | A member publisher lists the agent in their `adagents.json`. The registry crawler finds and indexes it automatically. The agent itself has not opted in. | `discovered` | Public only — always visible as a `public` agent, regardless of caller authentication |

## What trust level looks like in API responses

The `source` field is returned on every agent in `GET /api/registry/agents`.

```json Registered agent (member direct)
{
"url": "https://ads.streamhaus.example.com",
"type": "sales",
"source": "registered",
"member": { "slug": "streamhaus", "display_name": "StreamHaus" }
}
```

```json Discovered agent (via publisher adagents.json)
{
"url": "https://agent.examplepub.com",
"type": "sales",
"source": "discovered",
"discovered_from": { "publisher_domain": "examplepub.com" }
}
```

A `discovered` agent's `member` field is absent. It was listed by a publisher, not enrolled by the agent operator. If you are the operator of a `discovered` agent and want to upgrade to `registered` status, the path is AgenticAdvertising.org membership (see below).

## What AgenticAdvertising.org membership unlocks

Member organizations that enroll agents gain:

- **`registered` source** — callers can distinguish your agent from crawl-discovered entries
- **Visibility tiers** — choose `public` (default), `members_only` (visible only to authenticated AgenticAdvertising.org API callers), or `private` (visible only when you are the caller)
- **`member` field populated** — your organization name and slug appear alongside the agent in registry responses
- **Verified badge** — displayed in the public registry UI
- **Storyboard testing access** — run your agent against the AdCP compliance suite

To enroll as an AgenticAdvertising.org member, visit [agenticadvertising.org/join](https://agenticadvertising.org/join).

## Getting a non-member agent discovered

If you are not yet an AgenticAdvertising.org member, the only path into the registry is through a member publisher's `adagents.json`. Ask the publisher to add your agent URL with the appropriate `authorized_for` value. The registry crawler runs on a regular schedule and will pick up the entry on its next pass.

Self-registration without AgenticAdvertising.org membership is on the roadmap. A separate tracking issue will be filed once the trust-tier model for self-attested agents is decided.
2 changes: 1 addition & 1 deletion scripts/generate-openapi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ const TAG_DESCRIPTIONS: Record<string, string> = {
"Property Resolution": "Resolve publisher domains to their property configurations and authorized agents.",
"Agent Discovery": "Browse the federated agent network, search agent inventory profiles, publisher index, and registry statistics.",
"Change Feed": "Poll cursor-based registry change events for local sync.",
"Lookups & Authorization": "Look up agents by domain or property, and validate ad-serving authorization.",
"Authorization Lookups": "Look up agents by domain or property, and validate ad-serving authorization.",
"Validation Tools": "Validate publisher adagents.json files and generate compliant configurations.",
"Search": "Cross-entity search across brands, publishers, agents, and properties.",
"Agent Probing": "Connect to live agents and inspect their capabilities, formats, and inventory.",
Expand Down
2 changes: 1 addition & 1 deletion server/src/crawler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -577,7 +577,7 @@ export class CrawlerService {
this.snapshotDb.upsertHealth(agent.url, health, stats),
]);

if (!knownTypes.has(agent.url) && inferredType !== 'unknown') {
if (inferredType !== 'unknown' && inferredType !== knownTypes.get(agent.url)) {
await this.federatedIndex.updateAgentMetadata(agent.url, {
agent_type: inferredType,
protocol: profile.protocol,
Expand Down
26 changes: 15 additions & 11 deletions server/src/routes/registry-api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -608,14 +608,14 @@ registry.registerPath({
},
});

// Lookups & Authorization
// Authorization Lookups
registry.registerPath({
method: "get",
path: "/api/registry/lookup/domain/{domain}",
operationId: "lookupDomain",
summary: "Domain lookup",
description: "Find all agents authorized for a given publisher domain.",
tags: ["Lookups & Authorization"],
tags: ["Authorization Lookups"],
request: { params: z.object({ domain: z.string().openapi({ example: "examplepub.com" }) }) },
responses: {
200: { description: "Domain lookup result", content: { "application/json": { schema: DomainLookupResultSchema } } },
Expand All @@ -628,7 +628,7 @@ registry.registerPath({
operationId: "lookupProperty",
summary: "Property identifier lookup",
description: "Find agents that hold a specific property identifier.",
tags: ["Lookups & Authorization"],
tags: ["Authorization Lookups"],
request: { query: z.object({ type: z.string(), value: z.string() }) },
responses: {
200: { description: "Matching agents", content: { "application/json": { schema: z.object({ type: z.string(), value: z.string(), agents: z.array(z.unknown()), count: z.number().int() }) } } },
Expand All @@ -641,7 +641,7 @@ registry.registerPath({
operationId: "getAgentDomains",
summary: "Agent domain lookup",
description: "Get all publisher domains associated with an agent.",
tags: ["Lookups & Authorization"],
tags: ["Authorization Lookups"],
request: { params: z.object({ agentUrl: z.string() }) },
responses: {
200: { description: "Domains for the agent", content: { "application/json": { schema: z.object({ agent_url: z.string(), domains: z.array(z.string()), count: z.number().int() }) } } },
Expand All @@ -653,8 +653,12 @@ registry.registerPath({
path: "/api/registry/operator",
operationId: "lookupOperator",
summary: "Operator lookup",
description: "Given a domain, returns the agents this entity operates and which publishers trust them.",
tags: ["Lookups & Authorization"],
description:
"Given a domain, returns the agents this entity operates and which publishers trust them. " +
"Response visibility is caller-dependent: anonymous callers see public agents only; " +
"callers with an AgenticAdvertising.org API-access token additionally see members_only agents; " +
"callers who are the profile owner additionally see private agents.",
tags: ["Authorization Lookups"],
request: {
query: z.object({
domain: z.string().openapi({ example: "pubmatic.com" }),
Expand All @@ -672,7 +676,7 @@ registry.registerPath({
operationId: "lookupPublisher",
summary: "Publisher lookup",
description: "Given a domain, returns the inventory this entity publishes and which agents it authorizes.",
tags: ["Lookups & Authorization"],
tags: ["Authorization Lookups"],
request: {
query: z.object({
domain: z.string().openapi({ example: "voxmedia.com" }),
Expand All @@ -691,7 +695,7 @@ registry.registerPath({
summary: "Validate product authorization",
description:
"Check whether an agent is authorized to sell a product based on its publisher_properties.",
tags: ["Lookups & Authorization"],
tags: ["Authorization Lookups"],
request: {
body: {
content: {
Expand All @@ -715,7 +719,7 @@ registry.registerPath({
operationId: "expandProductIdentifiers",
summary: "Expand product identifiers",
description: "Expand publisher_properties selectors into concrete property identifiers for caching.",
tags: ["Lookups & Authorization"],
tags: ["Authorization Lookups"],
request: {
body: {
content: {
Expand Down Expand Up @@ -753,7 +757,7 @@ registry.registerPath({
operationId: "validatePropertyAuthorization",
summary: "Property authorization check",
description: "Quick check if a property identifier is authorized for an agent. Optimized for real-time ad request validation.",
tags: ["Lookups & Authorization"],
tags: ["Authorization Lookups"],
request: {
query: z.object({
agent_url: z.string(),
Expand Down Expand Up @@ -5160,7 +5164,7 @@ export function createRegistryApiRouter(config: RegistryApiConfig): Router {
}
});

// ── Lookups & Authorization ───────────────────────────────────
// ── Authorization Lookups ─────────────────────────────────────

router.get("/registry/operator", optAuth, async (req, res) => {
const rawDomain = req.query.domain as string;
Expand Down
20 changes: 17 additions & 3 deletions server/src/schemas/registry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -353,9 +353,23 @@ export const FederatedAgentWithDetailsSchema = z
})
.optional(),
added_date: z.string().optional(),
source: z.enum(["registered", "discovered"]).optional(),
member: MemberRefSchema.optional(),
discovered_from: DiscoveredFromSchema.optional(),
source: z
.enum(["registered", "discovered"])
.optional()
.openapi({
description:
"How this agent entered the registry. 'registered' means the operator directly enrolled " +
"via AgenticAdvertising.org and has accepted its terms. 'discovered' means the agent was " +
"found in a member publisher's adagents.json and has not explicitly opted in.",
}),
member: MemberRefSchema.optional().openapi({
description:
"AgenticAdvertising.org member organization that registered this agent. Present only when source='registered'.",
}),
discovered_from: DiscoveredFromSchema.optional().openapi({
description:
"The member publisher whose adagents.json referenced this agent. Present only when source='discovered'.",
}),
health: AgentHealthSchema.optional(),
stats: AgentStatsSchema.optional(),
capabilities: AgentCapabilitiesSchema.optional(),
Expand Down
18 changes: 9 additions & 9 deletions static/openapi/registry.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2579,7 +2579,7 @@ paths:
summary: Domain lookup
description: Find all agents authorized for a given publisher domain.
tags:
- Lookups & Authorization
- Authorization Lookups
parameters:
- schema:
type: string
Expand All @@ -2600,7 +2600,7 @@ paths:
summary: Property identifier lookup
description: Find agents that hold a specific property identifier.
tags:
- Lookups & Authorization
- Authorization Lookups
parameters:
- schema:
type: string
Expand Down Expand Up @@ -2640,7 +2640,7 @@ paths:
summary: Agent domain lookup
description: Get all publisher domains associated with an agent.
tags:
- Lookups & Authorization
- Authorization Lookups
parameters:
- schema:
type: string
Expand Down Expand Up @@ -2673,7 +2673,7 @@ paths:
summary: Operator lookup
description: Given a domain, returns the agents this entity operates and which publishers trust them.
tags:
- Lookups & Authorization
- Authorization Lookups
parameters:
- schema:
type: string
Expand All @@ -2700,7 +2700,7 @@ paths:
summary: Publisher lookup
description: Given a domain, returns the inventory this entity publishes and which agents it authorizes.
tags:
- Lookups & Authorization
- Authorization Lookups
parameters:
- schema:
type: string
Expand All @@ -2727,7 +2727,7 @@ paths:
summary: Validate product authorization
description: Check whether an agent is authorized to sell a product based on its publisher_properties.
tags:
- Lookups & Authorization
- Authorization Lookups
requestBody:
content:
application/json:
Expand Down Expand Up @@ -2768,7 +2768,7 @@ paths:
summary: Expand product identifiers
description: Expand publisher_properties selectors into concrete property identifiers for caching.
tags:
- Lookups & Authorization
- Authorization Lookups
requestBody:
content:
application/json:
Expand Down Expand Up @@ -2834,7 +2834,7 @@ paths:
summary: Property authorization check
description: Quick check if a property identifier is authorized for an agent. Optimized for real-time ad request validation.
tags:
- Lookups & Authorization
- Authorization Lookups
parameters:
- schema:
type: string
Expand Down Expand Up @@ -6000,7 +6000,7 @@ tags:
description: Browse the federated agent network, search agent inventory profiles, publisher index, and registry statistics.
- name: Change Feed
description: Poll cursor-based registry change events for local sync.
- name: Lookups & Authorization
- name: Authorization Lookups
description: Look up agents by domain or property, and validate ad-serving authorization.
- name: Validation Tools
description: Validate publisher adagents.json files and generate compliant configurations.
Expand Down
Loading