diff --git a/README.md b/README.md
index 6c808ce3..9e9dff76 100644
--- a/README.md
+++ b/README.md
@@ -31,7 +31,7 @@ The seller agent exposes four communication interfaces:
|-----------|----------|----------|
| **MCP** | `/mcp` (Streamable HTTP), `/mcp-sse/sse` (legacy) | Primary interface — 46 tools for Claude, ChatGPT, Codex, Cursor, and buyer agents |
| **A2A** | `/a2a/{agent}/jsonrpc` | Conversational JSON-RPC 2.0 for natural language queries |
-| **REST** | `/api/v1/*` | Programmatic access — 87 endpoints across 25 groups |
+| **REST** | `/api/v1/*` | Programmatic access — 89 endpoints across 25 groups |
| **Chat** | `/chat` | Web-based conversational interface for human buyers |
> [Protocol Documentation](https://iabtechlab.github.io/seller-agent/api/mcp/)
@@ -215,7 +215,7 @@ INDEX_EXCHANGE_API_URL=https://api.indexexchange.com
## API Reference
-87 endpoints across 25 groups:
+89 endpoints across 25 groups:
| Group | Endpoints | Description |
|-------|-----------|-------------|
@@ -223,7 +223,7 @@ INDEX_EXCHANGE_API_URL=https://api.indexexchange.com
| Packages | 7 | Tier-gated package CRUD, assembly, and sync |
| Products | 6 | Product catalog, avails, inventory type overrides |
| Quotes | 2 | Non-binding price quotes (IAB Deals API) |
-| Deal Booking | 11 | Deals, from-template, push, distribute, migrate, deprecate, lineage, export |
+| Deal Booking | 12 | Deals, list, from-template, push, distribute, migrate, deprecate, lineage, export |
| Deals | 1 | Deal creation from accepted proposals |
| Deal Performance | 1 | Delivery metrics |
| Bulk Operations | 1 | Batch deal create/update/cancel |
@@ -238,7 +238,7 @@ INDEX_EXCHANGE_API_URL=https://api.indexexchange.com
| Supply Chain | 1 | sellers.json-like self-description |
| Curators | 4 | Curator registration + curated deals |
| Sessions | 5 | Multi-turn session persistence |
-| Authentication | 4 | API key management |
+| Authentication | 5 | API key management |
| Agent Registry | 6 | Agent card, trust + discovery |
| Pricing | 3 | Rate card + pricing calculation |
| Events | 2 | Event log queries |
diff --git a/docs/api/openapi.json b/docs/api/openapi.json
index 4711c219..4730f858 100644
--- a/docs/api/openapi.json
+++ b/docs/api/openapi.json
@@ -1016,7 +1016,7 @@
"Products"
],
"summary": "List Products",
- "description": "List products in the catalog (shared ProductListResponse).\n\nReads from the cached static catalog (see `_get_static_product_catalog`)\ninstead of running ProductSetupFlow per request \u2014 kicking off the flow\nspins up an OpenDirect MCP session that hangs in `session.initialize()`.\nBuyers filter client-side over the returned Product records (there is\ndeliberately no POST /products/search on the shared catalog surface).",
+ "description": "List products in the catalog (shared ProductListResponse).\n\nReads from the cached static catalog (see `_get_static_product_catalog`)\ninstead of running ProductSetupFlow per request \u2014 kicking off the flow\nspins up an OpenDirect MCP session that hangs in `session.initialize()`.\nBuyers filter client-side over the returned Product records (there is\ndeliberately no POST /products/search on the shared catalog surface).\n\nAny stored inventory-type override (AI-14) is applied automatically \u2014\n``deps.get_product_catalog()`` itself now returns the catalog with\noverrides already applied, so every consumer sees one consistent view,\nnot just this route and ``get_product``.",
"operationId": "list_products_products_get",
"parameters": [
{
@@ -1131,7 +1131,7 @@
"Products"
],
"summary": "Get Product",
- "description": "Get a specific product (shared Product primitive, no wrapper).\n\nReads from the cached static catalog instead of running ProductSetupFlow\nper request (see `list_products` for rationale).",
+ "description": "Get a specific product (shared Product primitive, no wrapper).\n\nReads from the cached static catalog instead of running ProductSetupFlow\nper request (see `list_products` for rationale). Any stored\ninventory-type override (AI-14) is applied automatically by\n``deps.get_product_catalog()``.",
"operationId": "get_product_products__product_id__get",
"parameters": [
{
@@ -1937,6 +1937,86 @@
}
}
}
+ },
+ "get": {
+ "tags": [
+ "Deal Booking"
+ ],
+ "summary": "List Deals",
+ "description": "List stored deals, optionally filtered by wire status.\n\nOperator-only: the list spans every buyer's deals. Buyers read their\nown deal with ``GET /api/v1/deals/{deal_id}``.\n\n``status`` is the shared :class:`DealStatus` value the deal carries on\nthe wire (the same value ``GET /api/v1/deals/{deal_id}`` returns), so\nthe filter is applied after mapping each stored record to its\nresponse shape.\n\nRegistered ahead of ``/api/v1/deals/{deal_id}``; the two paths differ in\nlength so there is no shadowing, but keeping literal routes together\nwith ``/export`` keeps the EP-8.4 ordering rule easy to audit.",
+ "operationId": "list_deals_api_v1_deals_get",
+ "parameters": [
+ {
+ "name": "status",
+ "in": "query",
+ "required": false,
+ "schema": {
+ "anyOf": [
+ {
+ "$ref": "#/components/schemas/DealStatus"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "title": "Status"
+ }
+ },
+ {
+ "name": "authorization",
+ "in": "header",
+ "required": false,
+ "schema": {
+ "anyOf": [
+ {
+ "type": "string"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "title": "Authorization"
+ }
+ },
+ {
+ "name": "X-Api-Key",
+ "in": "header",
+ "required": false,
+ "schema": {
+ "anyOf": [
+ {
+ "type": "string"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "title": "X-Api-Key"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "Successful Response",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/DealListResponse"
+ }
+ }
+ }
+ },
+ "422": {
+ "description": "Validation Error",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/HTTPValidationError"
+ }
+ }
+ }
+ }
+ }
}
},
"/agentic-audience/match": {
@@ -7700,6 +7780,36 @@
"title": "DealFromTemplateResponse",
"description": "Response for template-based deal creation."
},
+ "DealListResponse": {
+ "properties": {
+ "deals": {
+ "items": {
+ "$ref": "#/components/schemas/DealBookingResponse"
+ },
+ "type": "array",
+ "title": "Deals"
+ },
+ "count": {
+ "type": "integer",
+ "title": "Count"
+ },
+ "skipped": {
+ "items": {
+ "type": "string"
+ },
+ "type": "array",
+ "title": "Skipped",
+ "default": []
+ }
+ },
+ "type": "object",
+ "required": [
+ "deals",
+ "count"
+ ],
+ "title": "DealListResponse",
+ "description": "Page of stored deals in the shared booking-response shape."
+ },
"DealMigrationRequest": {
"properties": {
"old_deal_id": {
diff --git a/docs/api/overview.md b/docs/api/overview.md
index 1c229f45..5430ee7c 100644
--- a/docs/api/overview.md
+++ b/docs/api/overview.md
@@ -1,6 +1,6 @@
# API Overview
-The Ad Seller System API exposes **87 endpoints** across **25 tags**. All endpoints are served from a single FastAPI application. The complete auto-generated route inventory is in the [REST Endpoints reference](../reference/endpoints.md).
+The Ad Seller System API exposes **89 endpoints** across **25 tags**. All endpoints are served from a single FastAPI application. The complete auto-generated route inventory is in the [REST Endpoints reference](../reference/endpoints.md).
**Base URL:** `http://localhost:8000`
**OpenAPI docs:** `http://localhost:8000/docs`
diff --git a/docs/architecture/overview.md b/docs/architecture/overview.md
index b7fca495..6ac8ec24 100644
--- a/docs/architecture/overview.md
+++ b/docs/architecture/overview.md
@@ -20,7 +20,7 @@ graph LR
subgraph "Seller Agent"
MCP_S["/mcp/ (Streamable HTTP)
MCP Server"]
A2A_S["/a2a/seller/jsonrpc
A2A Server"]
- REST_S["REST API
87 endpoints"]
+ REST_S["REST API
89 endpoints"]
NLP[NL Processing]
TOOLS[Seller Tools]
FLOWS[CrewAI Flows]
@@ -61,7 +61,7 @@ graph TB
subgraph "Protocol Layer"
MCP[MCP Server
/mcp/ Streamable HTTP]
A2A[A2A Server
/a2a/seller/jsonrpc]
- API[REST API
87 endpoints, 25 tags]
+ API[REST API
89 endpoints, 25 tags]
end
AUTH[Auth & API Keys]
@@ -135,7 +135,7 @@ graph TB
### API Layer
-**FastAPI application** with 87 endpoints across 25 OpenAPI tags. Handles HTTP routing, request validation, authentication, and response serialization. See [API Overview](../api/overview.md).
+**FastAPI application** with 89 endpoints across 25 OpenAPI tags. Handles HTTP routing, request validation, authentication, and response serialization. See [API Overview](../api/overview.md).
### Authentication and Agent Registry
diff --git a/docs/getting-started/quickstart.md b/docs/getting-started/quickstart.md
index 3d97b5fe..4f5cecd0 100644
--- a/docs/getting-started/quickstart.md
+++ b/docs/getting-started/quickstart.md
@@ -62,7 +62,7 @@ Expected response:
## Browse the API Docs
-Open `http://localhost:8000/docs` in a browser for the auto-generated Swagger UI with all 87 endpoints.
+Open `http://localhost:8000/docs` in a browser for the auto-generated Swagger UI with all 89 endpoints.
## First API Calls
@@ -118,6 +118,6 @@ Returns a confirmed deal with a Deal ID, OpenRTB parameters, and DSP activation
## Next Steps
-- [API Overview](../api/overview.md) --- see all 87 endpoints
+- [API Overview](../api/overview.md) --- see all 89 endpoints
- [Authentication](../api/authentication.md) --- set up API keys for authenticated access
- [Buyer Agent Integration](../integration/buyer-agent.md) --- connect a buyer agent
diff --git a/docs/guides/chatgpt-setup.md b/docs/guides/chatgpt-setup.md
index 689f93f1..a1df8eba 100644
--- a/docs/guides/chatgpt-setup.md
+++ b/docs/guides/chatgpt-setup.md
@@ -190,6 +190,6 @@ Base URL: https://your-publisher.example.com
Auth: Authorization: Bearer sk-operator-XXXXX
```
-The seller agent exposes 87 REST endpoints. See the [API Overview](../api/overview.md).
+The seller agent exposes 89 REST endpoints. See the [API Overview](../api/overview.md).
For ChatGPT specifically, you can also create a **Custom GPT** with Actions pointing to the REST API's OpenAPI spec at `https://your-publisher.example.com/openapi.json`.
diff --git a/docs/index.md b/docs/index.md
index 6a60c290..42bc2dc5 100644
--- a/docs/index.md
+++ b/docs/index.md
@@ -12,12 +12,12 @@ Part of the IAB Tech Lab Agent Ecosystem --- see also the [Buyer Agent](https://
|----------|----------|----------|
| **[MCP](api/mcp.md)** | `/mcp/` (Streamable HTTP), `/mcp-sse/sse` (legacy) | Primary interface — 46 tools for Claude, ChatGPT, Codex, Cursor, and buyer agents |
| **[A2A](api/a2a.md)** | *(planned — not yet served)* | Conversational agent interactions — natural language, multi-turn |
-| **[REST API](api/overview.md)** | `/api/v1/*` | Programmatic access — 87 endpoints across 25 groups |
+| **[REST API](api/overview.md)** | `/api/v1/*` | Programmatic access — 89 endpoints across 25 groups |
## Key Capabilities
- **46 MCP tools** for Claude, ChatGPT, Codex, Cursor, and Windsurf — interactive setup wizard + full operations
-- **87 REST endpoints** across 25 categories covering the complete ad selling workflow
+- **89 REST endpoints** across 25 categories covering the complete ad selling workflow
- **Pluggable ad server** support — Google Ad Manager and FreeWheel (Streaming Hub + Buyer Cloud)
- **Multi-SSP distribution** — PubMatic (MCP), Index Exchange (REST), Magnite (REST) with routing rules
- **IAB Deals API v1.0** — standardized deal push to buyer DSPs
@@ -54,7 +54,7 @@ Part of the IAB Tech Lab Agent Ecosystem --- see also the [Buyer Agent](https://
### API Reference
-- [API Overview](api/overview.md) --- all 87 endpoints grouped by tag
+- [API Overview](api/overview.md) --- all 89 endpoints grouped by tag
- [MCP Protocol](api/mcp.md) --- 46 MCP tools for Claude, ChatGPT, and buyer agents
- [A2A Protocol](api/a2a.md) --- conversational agent-to-agent interface
- [Agent Discovery](api/agent-discovery.md) --- `/.well-known/agent.json` and trust registry
diff --git a/tests/unit/test_openapi_drift.py b/tests/unit/test_openapi_drift.py
new file mode 100644
index 00000000..4023b416
--- /dev/null
+++ b/tests/unit/test_openapi_drift.py
@@ -0,0 +1,30 @@
+# Author: Green Mountain Systems AI Inc.
+# Donated to IAB Tech Lab
+
+"""Drift guard for the committed OpenAPI document.
+
+Builds the OpenAPI schema in memory the same way ``scripts/generate_openapi.py``
+does (``app.openapi()``) and asserts it equals the committed
+``docs/api/openapi.json``. Any code change that adds, removes, or reshapes a
+REST endpoint or schema breaks this test until the document is regenerated with:
+
+ python scripts/generate_openapi.py
+
+The comparison is on parsed JSON, so formatting alone cannot cause a false drift.
+"""
+
+import json
+from pathlib import Path
+
+from ad_seller.interfaces.api.main import app
+
+_COMMITTED = Path(__file__).resolve().parents[2] / "docs" / "api" / "openapi.json"
+
+
+def test_openapi_json_matches_app():
+ """Committed docs/api/openapi.json must equal the schema the app generates."""
+ assert _COMMITTED.exists(), f"{_COMMITTED} is missing — run: python scripts/generate_openapi.py"
+ assert json.loads(_COMMITTED.read_text()) == app.openapi(), (
+ "docs/api/openapi.json is out of date with the code.\n"
+ "Regenerate it with: python scripts/generate_openapi.py"
+ )