VEYNDO
Handbook
docs/b2b-ucp-negotiation.mdView source ↗

English source · 7 min

B2B negotiations through UCP

A buying agent can request an offer, exchange counterproposal messages, inspect a merchant's revised prices, accept that offer and request purchasing approval on a UCP checkout. VEYNDO's existing B2B quote, budget, tax, stock, payment and order owners execute the work. Studio's existing quote inbox shows the same RFQ; no second negotiation database or agent-only pricing engine exists.

What is standard and what is VEYNDO

UCP 2026-08-25 standardizes discovery, capability/version negotiation and extension composition. That capability negotiation selects compatible software contracts; it does not itself negotiate a product's price. Commercial negotiation has been discussed in the upstream proposal, but VEYNDO does not claim to implement a ratified dev.ucp.* negotiation capability.

Our discoverable extension is ai.veyndo.app.shopping.b2b_negotiation, version 2026-10-13, extending dev.ucp.shopping.checkout, version 2026-08-25. The schema is served from the corresponding namespace authority: https://app.veyndo.ai/ucp/2026-10-13/b2b-negotiation.json. Its $defs["dev.ucp.shopping.checkout"] composes with the official checkout schema using allOf. This is a selected VEYNDO UCP adapter, not whole-protocol certification. The immutable, secret-free schema has a precise public route; its admission exception does not include checkout sessions, future schemas or B2B commands. Shipping/payment terms still use native checkout selection; delivery contracts, credit agreements, autonomous price bargaining and AP2 mandates are not added here.

sequenceDiagram
    participant Agent as Authenticated buyer / agent
    participant UCP as VEYNDO UCP adapter
    participant B2B as Existing B2B owner
    participant Seller as Merchant quote inbox
    participant Approver as Separate purchasing approver
    participant Core as Shared checkout + outbox
    Agent->>UCP: Select extension, create checkout, select unit + PO
    UCP->>B2B: Current employee identity and purchasing policy
    Agent->>UCP: quote.request / quote.message
    UCP->>B2B: Native RFQ with original cart and currency
    B2B-->>Seller: Same quote, revision, conversation and event
    Seller->>B2B: quote.offer: integer unit prices + expiry
    Agent->>UCP: quote.get / quote.accept with both revisions
    UCP->>B2B: Recheck buyer, cart, unit, expiry, items and currency
    Agent->>UCP: approval.request
    Approver->>B2B: Approve exact current cart and policy
    Agent->>UCP: Complete checkout with Idempotency-Key
    UCP->>Core: Native locked budget / stock / payment guards
    Core-->>Agent: Same durable order and negotiated total
    Core-->>B2B: Consume quote and approval, allocate budget once
    Core-->>Seller: Ordinary order, receipt and Flow events

Setup and discovery

Configure a company, purchasing unit and existing customer employees in Studio. Give the buyer buy and quotes, and another employee approve if policy requires approval. The merchant uses b2b.manage; reading the merchant quote inbox additionally requires customers.pii. Full B2B setup.

Read GET /.well-known/ucp in the intended tenant/channel context. The checkout endpoint uses the configured Studio origin, retaining its tenant query; it no longer advertises localhost in hosted deployments. Calls retain native x-tenant, sw-sales-channel-id, x-customer-token and sw-context-token boundaries. A cart token or platform profile alone never signs in a company employee.

Two explicit opt-in transports are supported:

  • UCP profile: UCP-Agent: profile="https://agent.example/profile.json". The profile must advertise the exact checkout and extension versions, extension parent and published schema URL. Profiles are read over the shared public HTTPS transport: pinned public DNS, no private addresses, proxies, redirects or forwarded merchant/customer credentials; five-second request timeout and 32 KiB body limit. Missing parent/protocol compatibility fails; unsupported vendor versions cannot invoke B2B commands. There is no persistent platform-profile cache.
  • Pre-negotiated VEYNDO client: x-veyndo-ucp-b2b-version: 2026-10-13. This vendor-specific shortcut explicitly selects the known published contract. It is useful for direct clients and offline/local integration tests; it is not a replacement for UCP's interoperable platform-profile advertisement. Supplying both headers is rejected. Existing clients with neither retain the checkout slice and receive no negotiation capability or extension fields.

Example platform profile fragment:

{
  "ucp": {
    "version": "2026-08-25",
    "capabilities": {
      "dev.ucp.shopping.checkout": [{"version": "2026-08-25"}],
      "ai.veyndo.app.shopping.b2b_negotiation": [{
        "version": "2026-10-13",
        "extends": ["dev.ucp.shopping.checkout"],
        "schema": "https://app.veyndo.ai/ucp/2026-10-13/b2b-negotiation.json"
      }]
    }
  }
}

The fragment illustrates the adapter's selection inputs; supply a complete platform profile for other UCP implementations. The adapter supports this pinned protocol release rather than negotiating arbitrary older/future protocol versions.

Real commands, not another workflow engine

First create POST /ucp/v1/checkout-sessions with native UCP line_items, currency and buyer, retaining the returned context_token and checkout id. A current customer token binds the cart to that customer at creation.

Every buyer operation uses:

POST /ucp/v1/checkout-sessions/{checkoutId}/b2b/{operation}

Operation Native arguments and outcome
contexts {} — currently admitted company units
select unitId, purchaseOrder, revision — native company/cart selection
quote.request Current cart revision, message — new RFQ
quote.current {} — at most 20 current-cart RFQs, newest first
quote.get Quote id — one RFQ belonging to this buyer, unit and original cart
quote.message Quote id, current quote revision, message — buyer counterproposal
quote.reject Quote id, current quote revision, optional message
quote.accept Quote id, quote revision, cartRevision — bind the offered prices
quote.release Current cartRevision — release the accepted offer
approval.request Current cart revision — native purchasing approval request
checkout.handoff Current cart revision only — existing ten-minute single-use native checkout transfer

The response is the same checkout plus b2b_negotiation: checkout_revision, purchasing, current-cart quotes, optional quote_limit and the native command result. The native RFQ uses grossMinor per unit, with the unit's currency scale; UCP totals use the existing calculator's final currency amounts. VAT is not computed again in the protocol adapter. Final order prices remain their immutable checkout snapshot; terminal response purchasing metadata describes current company policy.

The merchant revises prices through the existing command:

POST /api/b2b/quote.offer
Authorization: Bearer <current merchant credential>
{
  "id": "<rfq-id>",
  "revision": 2,
  "message": "EUR 50 gross per lamp, warranty as agreed.",
  "data": {
    "lines": [{"id": "lamp", "quantity": 2, "grossMinor": 5000}],
    "validUntil": "2026-11-01T12:00:00Z"
  }
}

Use a future expiry within one year. Each counterproposal or offer increments the same RFQ revision. A different authorized approver uses the existing /store-api/b2b/approval.decide or matching MCP command. The buyer UCP route cannot issue merchant offers, change company grants or approve its own purchase.

Complete through POST /ucp/v1/checkout-sessions/{id}/complete with Idempotency-Key. The native locked transaction rechecks current budget, approval, policy, catalog, tax, stock and payment methods. Approval/budget problems are exposed as requires_escalation with stable b2b_approval_required / b2b_budget_exceeded message codes. Readiness is informational, not a stock/budget reservation or a promise that a later concurrent purchase will succeed. Canonical continuation/order links preserve the tenant and channel. To transfer the actual cart to a person, explicitly request checkout.handoff and open its returned continue_url. The secret remains in the URL fragment; consumption requires the same customer and rotates the cart token. Generic GET continuation links are navigation only and carry no cart/session credential. A handoff is not purchase approval or payment evidence.

API, MCP, apps, events, history and staging

b2b.quote.get and b2b.quote.current are also registered native Store API/MCP commands. The UCP adapter calls those same domain operations; app and Flow event delivery retains existing consent, scope and PII projections. Standard b2b.quote_requested, b2b.quote_changed, b2b.quote_selected and b2b.purchase_completed events use the existing transactional outbox. Studio's quote conversation and native history show those same versions and actors.

Company/unit configuration can be privately staged and selectively published. RFQs, conversations, approvals and spent budgets are live operational records; they cannot be cloned/released or rolled back as configuration. No app files move into core directories. Native B2B is core; protocol adapters live in src/ucp/.

Verification and boundaries

python3 scripts/verify_integration.py --only protocols b2b b2b_ucp tenant_isolation uses a disposable real PostgreSQL database. The B2B/UCP test executes discovery, version denial, private-profile target denial, employee selection, RFQ messages, merchant counteroffer, stale revisions, foreign buyer/tenant/original-cart denial, separate approval, native total parity, an idempotent simulated order, inventory movement, terminal quote and immediate membership revocation. Existing B2B tests cover budget contention, catalog restrictions, history and staging.

Rust tests cover profile parsing/intersection and operation/readiness decisions. No actual charge or autonomous LLM negotiation is claimed by those tests. Real external agent profile hosts and production payment credentials require separate acceptance; simulated checkout does not prove payment-provider settlement.