Skip to content
SDB
Agentic AI

Chapter 12 · advanced · 30 min

UCP — Universal Commerce Protocol

The emerging standard for agents that autonomously transact, negotiate, and conduct business

Subhendu Datta BhowmikAI Tutorials

Agents That Spend Money

As agents become more capable, they increasingly need to take actions in the real economy: purchasing API credits, hiring other agents, buying data, paying for compute resources, or contracting services.

Today this is handled ad hoc — each team writes custom billing integrations. Universal Commerce Protocol (UCP) is an emerging standard that defines how agents conduct business with each other and with human-facing services in a structured, auditable way.

UCP is built on the insight that commerce between agents follows a universal pattern regardless of what is being transacted:

Discovery → Negotiation → Agreement → Payment → Delivery → Receipt

Standardizing this flow lets agents from different organizations trade autonomously, with each step cryptographically signed and auditable.

UCP Core Primitives

PrimitiveDescription
ListingA service or good an agent offers for sale, with pricing and terms
Request for Quote (RFQ)Buyer agent asks seller agent for a price
OfferSeller's binding quote: price, terms, delivery, expiry
AcceptanceBuyer commits to the offer (creates a Contract)
ContractBinding agreement between two agents with all terms
Payment IntentInstruction to transfer value (fiat, crypto, credits)
DeliveryThe agreed service or goods, with proof of delivery
ReceiptSigned confirmation that delivery was received and payment made
UCP Data Modelspython
# ucp_models.py — UCP core data structures
from pydantic import BaseModel, Field
from typing import Optional, Literal
from datetime import datetime
import uuid

class Price(BaseModel):
    amount: float
    currency: str  # "USD", "EUR", "USDC", "credits"
    unit: str      # "per-call", "per-token", "per-hour", "flat"

class ServiceListing(BaseModel):
    """A service an agent offers for sale."""
    id: str = Field(default_factory=lambda: str(uuid.uuid4()))
    provider_did: str
    name: str
    description: str
    category: str  # "research", "coding", "analysis", "compute"
    price: Price
    terms: dict    # usage limits, SLAs, refund policy
    capabilities: list[str]
    created_at: datetime = Field(default_factory=datetime.utcnow)
    expires_at: Optional[datetime] = None

class RequestForQuote(BaseModel):
    """Buyer agent asks for a price for a specific service."""
    id: str = Field(default_factory=lambda: str(uuid.uuid4()))
    buyer_did: str
    seller_did: str
    service_category: str
    requirements: dict  # what the buyer needs
    quantity: int = 1
    budget_cap: Optional[Price] = None  # buyer's maximum willingness to pay
    deadline: Optional[datetime] = None

class Offer(BaseModel):
    """Seller's binding quote in response to an RFQ."""
    id: str = Field(default_factory=lambda: str(uuid.uuid4()))
    rfq_id: str
    seller_did: str
    buyer_did: str
    listing_id: str
    price: Price
    terms: dict
    valid_until: datetime
    delivery_estimate: str  # "immediate", "2h", "24h"
    proof_of_capability: Optional[str] = None  # URL to demo or sample

class ContractAcceptance(BaseModel):
    """Buyer accepts an offer — creates a binding contract."""
    id: str = Field(default_factory=lambda: str(uuid.uuid4()))
    offer_id: str
    buyer_did: str
    accepted_at: datetime = Field(default_factory=datetime.utcnow)
    payment_method: str  # "stripe", "crypto", "credits", "invoice"
    payment_reference: Optional[str] = None

class Contract(BaseModel):
    """The binding agreement derived from acceptance."""
    id: str = Field(default_factory=lambda: str(uuid.uuid4()))
    offer: Offer
    acceptance: ContractAcceptance
    status: Literal["active", "completed", "disputed", "canceled"] = "active"
    created_at: datetime = Field(default_factory=datetime.utcnow)
    buyer_signature: Optional[str] = None
    seller_signature: Optional[str] = None

class DeliveryReceipt(BaseModel):
    """Proof that service was delivered and received."""
    id: str = Field(default_factory=lambda: str(uuid.uuid4()))
    contract_id: str
    delivered_at: datetime = Field(default_factory=datetime.utcnow)
    delivery_proof: str  # hash or URL of delivered artifact
    buyer_confirmed: bool = False
    payment_settled: bool = False
Seller Agent — Publishing a Listing and Handling Offerspython
# ucp_seller.py — An agent that sells research services via UCP
from fastapi import FastAPI, HTTPException
from anthropic import Anthropic
from ucp_models import ServiceListing, RequestForQuote, Offer, Contract, DeliveryReceipt
from datetime import datetime, timedelta
import uuid, hashlib

app = FastAPI(title="Research Agent — UCP Seller")
anthropic = Anthropic()

SELLER_DID = "did:web:research-agent.acme.com"

# Published service listing
RESEARCH_LISTING = ServiceListing(
    id="listing-research-v1",
    provider_did=SELLER_DID,
    name="Research Report Service",
    description="Comprehensive web research and synthesis on any topic",
    category="research",
    price={"amount": 0.50, "currency": "USD", "unit": "per-call"},
    terms={
        "max_tokens_per_call": 4096,
        "sla": "p99 < 30s",
        "refund_policy": "full refund if quality score < 7/10",
        "usage_restrictions": "no harmful content"
    },
    capabilities=["web-search", "synthesis", "citation", "structured-output"],
    expires_at=datetime.utcnow() + timedelta(days=30)
)

# -------------------------------------------------------
# UCP Endpoints
# -------------------------------------------------------
@app.get("/ucp/listings")
async def get_listings():
    """Discovery: publish what this agent sells."""
    return {"listings": [RESEARCH_LISTING.model_dump()]}

@app.post("/ucp/rfq")
async def handle_rfq(rfq: RequestForQuote):
    """Receive a Request for Quote and return a binding offer."""
    # Validate the buyer's requirements
    requirements = rfq.requirements
    estimated_complexity = requirements.get("depth", "medium")

    # Dynamic pricing based on complexity
    price_map = {"quick": 0.25, "medium": 0.50, "deep": 1.50}
    unit_price = price_map.get(estimated_complexity, 0.50)

    # Check budget cap
    if rfq.budget_cap and rfq.budget_cap["amount"] < unit_price:
        raise HTTPException(
            status_code=400,
            detail=f"Budget cap ${rfq.budget_cap['amount']} below minimum price ${unit_price}"
        )

    offer = Offer(
        rfq_id=rfq.id,
        seller_did=SELLER_DID,
        buyer_did=rfq.buyer_did,
        listing_id=RESEARCH_LISTING.id,
        price={"amount": unit_price, "currency": "USD", "unit": "per-call"},
        terms=RESEARCH_LISTING.terms,
        valid_until=datetime.utcnow() + timedelta(hours=1),
        delivery_estimate="immediate"
    )
    return offer.model_dump()

@app.post("/ucp/contracts")
async def create_contract(acceptance):
    """Buyer accepted an offer — create the contract and deliver service."""
    contract_id = str(uuid.uuid4())

    # In production: verify payment before delivering
    # payment_verified = await verify_payment(acceptance["payment_reference"])

    # Deliver the service
    topic = acceptance.get("requirements", {}).get("topic", "general topic")
    response = anthropic.messages.create(
        model="claude-sonnet-4-6",
        max_tokens=2048,
        messages=[{"role": "user", "content": f"Research and write a comprehensive report on: {topic}"}],
    )
    result = response.content[0].text

    # Create delivery proof (hash of content)
    delivery_hash = hashlib.sha256(result.encode()).hexdigest()

    receipt = DeliveryReceipt(
        contract_id=contract_id,
        delivery_proof=delivery_hash,
        buyer_confirmed=False,
        payment_settled=True  # would be set after payment confirmation
    )

    return {
        "contract_id": contract_id,
        "status": "completed",
        "delivery": {"content": result, "proof_hash": delivery_hash},
        "receipt": receipt.model_dump()
    }
Buyer Agent — Discovering, Negotiating, and Purchasingpython
# ucp_buyer.py — An agent that purchases research services via UCP
import httpx, asyncio
from anthropic import Anthropic
from ucp_models import RequestForQuote
from datetime import datetime, timedelta
import uuid

anthropic = Anthropic()
BUYER_DID = "did:web:buyer-agent.mycompany.com"
SELLER_URL = "http://localhost:8002"

# Safety: human approval required for any payment above this
AUTONOMOUS_PAYMENT_LIMIT = 1.00  # USD

async def discover_services(seller_url: str) -> list[dict]:
    """Find what services a seller agent offers."""
    async with httpx.AsyncClient() as client:
        resp = await client.get(f"{seller_url}/ucp/listings")
        listings = resp.json()["listings"]
        print(f"Found {len(listings)} services:")
        for listing in listings:
            print(f"  - {listing['name']}: ${listing['price']['amount']}/{listing['price']['unit']}")
        return listings

async def request_quote(seller_url: str, topic: str, depth: str = "medium") -> dict:
    """Ask the seller for a price for a specific job."""
    rfq = RequestForQuote(
        buyer_did=BUYER_DID,
        seller_did="did:web:research-agent.acme.com",
        service_category="research",
        requirements={"topic": topic, "depth": depth},
        budget_cap={"amount": 2.00, "currency": "USD", "unit": "per-call"}
    )

    async with httpx.AsyncClient() as client:
        resp = await client.post(f"{seller_url}/ucp/rfq", json=rfq.model_dump())
        offer = resp.json()
        print(f"Offer received: ${offer['price']['amount']} {offer['price']['currency']}")
        print(f"Valid until: {offer['valid_until']}")
        return offer

async def accept_offer_and_purchase(seller_url: str, offer: dict,
                                     topic: str, human_approval_fn=None) -> dict:
    """Accept the offer, pay, and receive the service."""
    price = offer["price"]["amount"]

    # Safety gate: require human approval for larger purchases
    if price > AUTONOMOUS_PAYMENT_LIMIT:
        if human_approval_fn is None:
            raise PermissionError(
                f"Purchase of ${price} exceeds autonomous limit ${AUTONOMOUS_PAYMENT_LIMIT}. "
                f"Human approval required."
            )
        approved = await human_approval_fn(offer)
        if not approved:
            return {"status": "rejected", "reason": "Human declined the purchase"}

    acceptance = {
        "offer_id": offer["id"],
        "buyer_did": BUYER_DID,
        "accepted_at": datetime.utcnow().isoformat(),
        "payment_method": "credits",
        "payment_reference": f"pay_{uuid.uuid4().hex[:8]}",
        "requirements": {"topic": topic}
    }

    async with httpx.AsyncClient(timeout=60) as client:
        resp = await client.post(f"{seller_url}/ucp/contracts", json=acceptance)
        result = resp.json()
        print(f"Service delivered! Proof hash: {result['delivery']['proof_hash'][:16]}...")
        return result

async def main():
    topic = "The economic impact of agentic AI on knowledge work"

    print("=== 1. Discover available services ===")
    listings = await discover_services(SELLER_URL)

    print("\n=== 2. Request a quote ===")
    offer = await request_quote(SELLER_URL, topic, depth="medium")

    print("\n=== 3. Accept and purchase ===")
    result = await accept_offer_and_purchase(SELLER_URL, offer, topic)

    print("\n=== 4. Delivered content ===")
    print(result["delivery"]["content"][:500] + "...")

asyncio.run(main())

Safety Patterns for Commerce Agents

Agents that can spend money require strict safety controls. Never build a commerce agent without all of these:

Mandatory Safety Controls

ControlImplementation
Spending limitsHard cap on autonomous spending per day/transaction
Human approval gatesRequire human sign-off above threshold
Allowlist vendorsOnly transact with pre-approved seller DIDs
Budget trackingDeduct from a budget pool, stop when empty
Audit logEvery transaction signed, timestamped, immutable
ReversibilityPrefer services that allow refunds/cancellation
IdempotencyNever pay twice for the same service

Example Budget Guard

class AgentBudgetGuard:
    def __init__(self, daily_limit: float, per_transaction_limit: float,
                 approved_vendors: list[str]):
        self.daily_limit = daily_limit
        self.per_transaction_limit = per_transaction_limit
        self.approved_vendors = set(approved_vendors)
        self.spent_today = 0.0

    def can_purchase(self, price: float, vendor_did: str) -> tuple[bool, str]:
        if vendor_did not in self.approved_vendors:
            return False, f"Vendor {vendor_did} not in approved list"
        if price > self.per_transaction_limit:
            return False, f"Price ${price} exceeds per-transaction limit"
        if self.spent_today + price > self.daily_limit:
            return False, f"Would exceed daily budget of ${self.daily_limit}"
        return True, "approved"

    def record_purchase(self, price: float):
        self.spent_today += price

The Agent Economy

UCP enables an emerging agent economy where:

  • Specialized agents sell their capabilities to generalist orchestrators
  • Compute marketplaces let agents bid for GPU time
  • Data brokers sell verified datasets to agent pipelines
  • Quality validators certify agent outputs for a fee

This creates network effects: the more agents adopt UCP, the richer the ecosystem each agent can access.

Knowledge check

Which safety control is MOST critical for an agent authorized to spend money autonomously?

Summary

UCP standardizes how agents conduct commerce:

  1. Listings — agents publish what they sell, at what price, under what terms
  2. RFQ / Offer cycle — structured negotiation before committing
  3. Contracts — binding, signed agreements between agents
  4. Safety gates — spending limits and human approval prevent runaway purchases
  5. Agent economy — a marketplace where specialized agents sell capabilities to each other

In the next chapter, we'll explore TDF (Task Definition Framework) — the standard for expressing what an agent should do in a structured, unambiguous, and interoperable way.

Agentic AI