Authentication¶
The seller agent supports authenticated and anonymous access. Authentication unlocks tiered pricing, negotiation, and richer data in responses. Operator credentials unlock the control plane (key management, rate card, registry trust, packages, inventory sync).
Authentication Methods¶
Two methods are accepted. Both can be used on any endpoint:
Bearer Token¶
API Key Header¶
When both headers are present, the system validates whichever is found first. Anonymous requests (no key) are allowed on most buyer-facing endpoints but receive public-tier access only.
Key Roles¶
Every API key has a role:
| Role | Purpose |
|---|---|
buyer |
Buyer-agent credential. Grants tiered data access (seat/agency/advertiser pricing). No control-plane rights. |
operator |
Publisher operator credential. Required for admin REST endpoints and admin MCP tools over HTTP. |
Pre-existing keys (stored before the role field existed) deserialize as buyer — they are never silently promoted to operator.
Bootstrap: First Operator Key¶
Creating keys via the HTTP API itself requires an operator credential. Mint the first operator key out-of-band with the CLI (writes directly to storage — no network surface):
Run this with the same storage config (.env) as the server so the key lands in the backend the server reads. The full key is printed once — store it securely.
List operator keys (metadata only — secrets are never re-shown):
ad-seller list-operator-keys
ad-seller list-operator-keys --include-inactive # include revoked/expired
To revoke an operator key out-of-band (frees the label for reuse):
ad-seller delete-operator-key --label "Primary operator"
# or
ad-seller delete-operator-key --key-id key-a1b2c3d4
Subsequent operator keys can be minted over HTTP with an existing operator credential (see below).
Operator Surface¶
These routes require a valid operator key (anonymous → 401, buyer key → 403):
- All
/auth/api-keysroutes (create buyer, create operator, list, get, revoke) /events,/events/{id}PUT /api/v1/rate-cardPOST /api/v1/inventory-sync/triggerGET /gam/orders,GET /gam/report- Registry mutations: discover, trust update, delete
- Package mutations:
POST/PUT/DELETE /packages,/packages/assemble,/packages/sync POST /api/v1/curators,POST /api/v1/deals/push,POST /api/v1/deals/distribute
Buyer-facing reads (GET /packages, GET /registry/agents, GET /api/v1/rate-card, media-kit search, etc.) stay public or buyer-authenticated as before.
Admin MCP tools over HTTP (Streamable HTTP / SSE) enforce the same operator check via Authorization / X-Api-Key. Local stdio MCP access is trusted like the CLI.
API Key Lifecycle¶
All key-management endpoints below require an operator credential.
Create a Buyer Key¶
curl -X POST http://localhost:8000/auth/api-keys \
-H "Authorization: Bearer <operator_api_key>" \
-H "Content-Type: application/json" \
-d '{
"seat_id": "seat-acme-001",
"seat_name": "Acme DSP",
"agency_id": "agency-mega",
"agency_name": "Mega Agency",
"advertiser_id": "adv-widget-co",
"advertiser_name": "Widget Co",
"label": "Widget Co production key",
"expires_in_days": 365
}'
This endpoint always creates a buyer key. There is no role field — operator keys use a separate endpoint.
The response contains the full API key which is shown only once. Store it securely --- it cannot be retrieved again.
Create an Operator Key¶
curl -X POST http://localhost:8000/auth/api-keys/operator \
-H "Authorization: Bearer <operator_api_key>" \
-H "Content-Type: application/json" \
-d '{
"label": "Ops secondary key",
"expires_in_days": 365
}'
Operator keys carry no buyer identity (no seat/agency/advertiser fields) — only label and optional expires_in_days.
List Keys¶
Returns metadata for all keys (no secrets). Includes key ID, label, role, identity, creation date, and status.
Get Key Details¶
Revoke a Key¶
curl -X DELETE http://localhost:8000/auth/api-keys/{key_id} \
-H "Authorization: Bearer <operator_api_key>"
Revoked keys immediately return HTTP 401 on use.
Access Tiers¶
Access tiers control pricing visibility, discount eligibility, and negotiation access for buyer keys:
| Tier | Description | Pricing Visibility | Negotiation |
|---|---|---|---|
public |
Anonymous / unknown buyer | Price ranges only | No |
seat |
Identified DSP seat | Exact prices, no discounts | Limited |
agency |
Agency-level identity | Tier discounts applied | Standard |
advertiser |
Full advertiser identity | Full discounts + volume | Premium |
The tier is determined automatically from the API key's identity fields. If no key is provided, the tier falls back to public (or to the buyer_tier body parameter for backward compatibility).
Agent Registry Trust Levels¶
When a buyer agent provides its agent_url, the seller looks up the agent in its registry and maps trust level to a maximum access tier:
| Trust Status | Description | Max Access Tier |
|---|---|---|
unknown |
Never seen before | public |
registered |
Fetched agent card, not yet verified | seat |
approved |
Manually approved by seller operator | advertiser |
preferred |
Trusted partner with custom pricing rules | advertiser |
blocked |
Rejected --- returns HTTP 403, zero data | None |
The effective tier is the minimum of the API key tier and the agent trust tier. A preferred agent with a seat-level API key gets seat access. A public API key with an approved agent gets public access.
Managing Trust¶
Registry mutations require an operator credential:
# Discover and register an agent
curl -X POST http://localhost:8000/registry/agents/discover \
-H "Authorization: Bearer <operator_api_key>" \
-H "Content-Type: application/json" \
-d '{"agent_url": "https://buyer.example.com"}'
# Approve the agent
curl -X PUT http://localhost:8000/registry/agents/{agent_id}/trust \
-H "Authorization: Bearer <operator_api_key>" \
-H "Content-Type: application/json" \
-d '{"trust_status": "approved", "notes": "Verified by ops team"}'
# Block a malicious agent
curl -X PUT http://localhost:8000/registry/agents/{agent_id}/trust \
-H "Authorization: Bearer <operator_api_key>" \
-H "Content-Type: application/json" \
-d '{"trust_status": "blocked", "notes": "Abuse detected"}'