Skip to content
Open
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 .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -24,3 +24,13 @@ REDIS_URL=
# Environment
ENVIRONMENT=development
LOG_LEVEL=INFO

# IAB Diligence Platform Integration (via SafeGuard Privacy)
# optional; inert when SGP_API_KEY is empty
SGP_API_KEY=
# Staging Environment: https://api.safeguardprivacy-demo.com
SGP_BASE_URL=https://api.safeguardprivacy.com
SGP_ENFORCE=false
# block | warn | allow
SGP_UNKNOWN_VENDOR_POLICY=block
SGP_CACHE_TTL_SECONDS=900
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,12 @@ Reveal buyer identity progressively to unlock better pricing from sellers:

→ [Authentication Guide](https://iabtechlab.github.io/buyer-agent/api/authentication/)

### Vendor Approval Gating (optional)

Plug in an [IAB Diligence Platform](https://safeguardprivacy.com) tenant to keep unapproved sellers out of the buyer-agent workflow. Consults the `iabBuyerAgentApproval` flag via SGP's integration API. When `SGP_ENFORCE=true`, `DiscoverInventoryTool` filters NOT APPROVED vendors out of search results before the agent ever sees them, and `RequestDealTool` enforces the same check as a safety net at Deal ID time. With enforcement off, products are annotated APPROVED / NOT APPROVED / UNKNOWN but never filtered. Off by default — inert when `SGP_API_KEY` is empty.

→ [IAB Diligence Platform Approval](https://iabtechlab.github.io/buyer-agent/integration/iab-diligence-platform/)

## Quick Start

### Install
Expand Down
16 changes: 16 additions & 0 deletions docs/guides/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,22 @@ CORS_ALLOWED_ORIGINS=https://dashboard.example.com,https://app.example.com

---

### IAB Diligence Platform Approval

Optional integration that gates deal requests against the buyer's [IAB Diligence Platform](https://safeguardprivacy.com/iab-diligence-platform/) vendor portfolio. Inert when `SGP_API_KEY` is empty.

| Variable | Type | Default | Description |
|----------|------|---------|-------------|
| `SGP_API_KEY` | `str` | `""` | API key with the `iab:buyerAgent` scope. Empty = integration disabled. |
| `SGP_BASE_URL` | `str` | `https://api.safeguardprivacy.com` | SGP base URL. Staging: `https://api.safeguardprivacy-demo.com`. |
| `SGP_ENFORCE` | `bool` | `False` | When `True`, NOT APPROVED vendors are filtered out at discovery and Deal ID generation is blocked for them. SGP transport errors halt the flow. |
| `SGP_UNKNOWN_VENDOR_POLICY` | `str` | `block` | Behavior when the vendor is not in the buyer's SGP portfolio (HTTP 404). One of `block`, `warn`, `allow`. |
| `SGP_CACHE_TTL_SECONDS` | `int` | `900` | Per-domain cache lifetime for approval lookups. |

See the [IAB Diligence Platform Approval](../integration/iab-diligence-platform.md) integration guide for endpoint contract, behavior matrix, and troubleshooting.

---

### Environment

| Variable | Type | Default | Description |
Expand Down
171 changes: 171 additions & 0 deletions docs/integration/iab-diligence-platform.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
# IAB Diligence Platform

The buyer agent can verify, before issuing a Deal ID, that the buyer has explicitly approved a seller's vendor record for IAB buyer-agent purchases. Approvals are stored in the buyer's [IAB Diligence Platform](https://safeguardprivacy.com/iab-diligence-platform/) tenant; the buyer agent consults them through SGP's integration API.

This integration is **optional and off by default**. When `SGP_API_KEY` is empty the feature is fully inert — the buyer agent behaves exactly as it did before this page existed. Once configured, it acts as a privacy rail in front of the existing deal workflow.

## Who should enable this

IAB Diligence Platform customers who treat vendor onboarding and approval as a compliance prerequisite for programmatic buying. If your team already maintains a vendor inventory in SGP with IAB buyer-agent approval flags, this integration enforces that workflow inside the buyer agent itself.

## Endpoint contract

The client calls a single endpoint on the IAB Diligence Platform (SafeGuard Privacy API):

```
GET /api/v1/integrations/iab/buyer-agent-approval?domain=a.com,b.com
```

| Property | Value |
|--------------|---------------------------------------------------------|
| Auth | `api-key` header |
| Domain | `domain` query parameter - Up to 10 domains per request |
| Tenant scope | Results are scoped to the caller's SGP tenant |

The response contains one `IabBuyerAgentResource` per matched vendor:

```json
{
"status": "success",
"code": 200,
"data": [
{
"vendorId": 123,
"vendorCompanyId": 456,
"companyName": "Example Publisher",
"domain": "example.com",
"iabBuyerAgentApproval": true,
"iabBuyerAgentApprovedAt": "2026-03-14T12:00:00Z"
}
]
}
```

Three response states matter to the buyer agent:

| State | Meaning | How the gate treats it |
|-------|---------|------------------------|
| `iabBuyerAgentApproval: true` | Buyer has approved this vendor | ✅ Deal proceeds |
| `iabBuyerAgentApproval: false` | Vendor exists but is not approved | ❌ Deal blocked |
| HTTP 404 | Vendor is not in the buyer's SGP portfolio | Governed by `SGP_UNKNOWN_VENDOR_POLICY` |

## Configuration

| Variable | Type | Default | Description |
|----------|------|---------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `SGP_API_KEY` | `str` | `""` | API key from the SGP api. Empty = integration disabled. |
| `SGP_BASE_URL` | `str` | `https://api.safeguardprivacy.com` | Production endpoint. The staging environment is `https://api.safeguardprivacy-demo.com`. |
| `SGP_ENFORCE` | `bool` | `False` | When `True`, NOT APPROVED vendors are filtered out at discovery, the deal-request gate blocks Deal ID generation, and SGP transport errors halt the flow. |
| `SGP_UNKNOWN_VENDOR_POLICY` | `str` | `"block"` | Behavior for domains not in the SGP portfolio (HTTP 404). One of `block`, `warn`, `allow`. Applies at both discovery and deal-request stages when enforcement is on. |
| `SGP_CACHE_TTL_SECONDS` | `int` | `900` | Per-domain cache lifetime. Discovery→pricing→booking reuse a single SGP call within the TTL. |

!!! warning "Enforcement without a key is a no-op"
If `SGP_ENFORCE=true` but `SGP_API_KEY` is empty, the gate cannot be evaluated and is silently bypassed. The buyer agent logs a warning at flow construction time so this misconfiguration is visible.

## Where the gate runs

The integration plugs into two existing buyer-agent tools. Behavior at each stage is governed by the same `SGP_ENFORCE` flag.

### Inventory discovery

`DiscoverInventoryTool` accepts an optional `SGPClient`. When provided, it extracts the seller domain from each returned product (checking `seller_url`, `publisher_domain`, then `publisherId`/`publisher` if they contain a `.`), batches distinct domains into groups of 10, and annotates each product row in the formatted output:

```
1. Premium CTV - Sports
Product ID: ctv-premium-sports
Publisher: premium-pub-001
CPM: $28.26 (was $35.00)
SGP Approval: ✓ APPROVED — seller.example.com
```

Behavior depends on `SGP_ENFORCE`:

| `SGP_ENFORCE` | NOT APPROVED rows | Unknown vendors | Missing seller domain | SGP transport error |
|---|---|---|---|---|
| `false` (annotate only) | kept + annotated | kept + annotated | kept (no annotation) | logged, no annotations |
| `true` (filter) | **filtered out** | governed by `SGP_UNKNOWN_VENDOR_POLICY` | filtered out | flow halts (fails closed) |

When enforcement removes any products, a tail line is appended so the action is auditable:

```
--------------------------------------------------
Total products found: 4
SGP enforcement filtered 2 product(s): 1 not approved, 1 unknown to SGP
```

### Deal-request gate

`RequestDealTool` checks the seller's vendor approval after fetching product details and before generating a Deal ID. The gate acts as a safety net behind discovery filtering — it runs only when an `SGPClient` is wired in and `sgp_enforce=True`:

```python
# Injected automatically by BuyerDealFlow from settings
RequestDealTool(
client=unified_client,
buyer_context=ctx,
sgp_client=sgp_client,
sgp_enforce=settings.sgp_enforce,
sgp_unknown_policy=settings.sgp_unknown_vendor_policy,
)
```

A successful gate prepends a banner to the Deal ID response:

```
SGP: ✓ Example Publisher approved for IAB buyer-agent purchases (since 2026-03-14T12:00:00Z).

============================================================
DEAL CREATED SUCCESSFULLY
============================================================
...
```

A failed gate returns a blocking message and does **not** generate a Deal ID.

## Behavior matrix

With enforcement on (`SGP_ENFORCE=true`, `SGP_API_KEY` set), behavior is consistent across stages:

| SGP response | `block` policy | `warn` policy | `allow` policy |
|---|---|---|---|
| `iabBuyerAgentApproval: true` | ✅ kept + approved banner | same | same |
| `iabBuyerAgentApproval: false` | ❌ filtered at discovery; blocked at request | ❌ | ❌ |
| 404 (not onboarded in SGP) | ❌ filtered at discovery; blocked at request | ✅ kept + warning annotation/banner | ✅ kept silently |
| Transport error | ❌ flow halts | ❌ flow halts | ❌ flow halts |
| Product has no seller domain field | ❌ filtered at discovery; blocked at request | ❌ | ❌ |

The `iabBuyerAgentApproval: false` row is intentionally the same across all three unknown-vendor policies — an explicit non-approval is always fatal. The policies only govern the "unknown to SGP" case.

## Agent tool

For CrewAI agents that want to consult approvals outside the automatic gate, a tool is provided:

```python
from ad_buyer.clients import SGPClient
from ad_buyer.tools.research import SGPVendorApprovalTool

sgp = SGPClient(api_key=settings.sgp_api_key, base_url=settings.sgp_base_url)
tool = SGPVendorApprovalTool(client=sgp)

# Agent calls it with a list of domains (any number; client chunks to 10)
# Returns a formatted APPROVED / NOT APPROVED / UNKNOWN summary.
```

`BuyerDealFlow` injects this tool into the Buyer Deal Specialist automatically when an SGP client is configured, so the agent can consult approval status during product selection (before commitment), not only at Deal ID generation time.

The class is prefixed `SGP` so future vendor-approval integrations can coexist under their own class names and CrewAI `name` attributes without colliding.

## Troubleshooting

| Symptom | Likely cause |
|---------|------------------------------------------------------------------------------------------------------------------------------------------------|
| `IAB Diligence Platform rejected the api-key` (401) | The key is missing, revoked, or lacks the proper scope. Request a new key from SGP. |
| `Deal blocked: <domain> is not in your IAB Diligence Platform portfolio` | The vendor is not onboarded in SGP. Add and approve the vendor in SGP, or switch `SGP_UNKNOWN_VENDOR_POLICY` to `warn` for soft-fail behavior. |
| `Deal blocked: <vendor> does not carry the IAB buyer-agent approval flag` | The vendor is onboarded but not marked approved for IAB buyer-agent purchases. Toggle the approval in SGP. |
| `Deal blocked: IAB Diligence Platform lookup failed` | SGP was unreachable or returned a transient error. Enforcement fails closed; retry once the service is reachable. |
| Gate seems to do nothing | Either `SGP_API_KEY` is empty or `SGP_ENFORCE=false`. Check startup logs for the bypass warning. |

## Related

- [Configuration reference](../guides/configuration.md) — all env vars including SGP
- [Buyer Deal Flow](../architecture/buyer-deal-flow.md) — the flow the gate plugs into
- [Seller Agent Integration](seller-agent.md) — the seller side of the deal request
1 change: 1 addition & 0 deletions mkdocs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@ nav:
- Integration:
- Seller Agent Guide: integration/seller-agent.md
- OpenDirect Protocol: integration/opendirect.md
- IAB Diligence Platform Approval: integration/iab-diligence-platform.md
- AI Assistant Setup:
- Claude (Desktop & Web): claude-desktop-setup.md
- ChatGPT, Codex & AI IDEs: multi-client-setup.md
Expand Down
5 changes: 5 additions & 0 deletions src/ad_buyer/clients/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
from .deals_client import DealsClient, DealsClientError
from .mcp_client import IABMCPClient, MCPClientError, MCPToolResult
from .opendirect_client import OpenDirectClient
from .sgp_client import SGPAuthError, SGPClient, SGPClientError
from .ucp_client import UCPClient, UCPExchangeResult
from .unified_client import Protocol, UnifiedClient, UnifiedResult

Expand All @@ -31,4 +32,8 @@
# IAB Deals API v1.0 client (quote-then-book flow)
"DealsClient",
"DealsClientError",
# IAB Diligence Platform (SGP) approval gate
"SGPClient",
"SGPClientError",
"SGPAuthError",
]
Loading