Developers/Transaction lifecycle
Concepts

Transaction lifecycle

One deal, start to finish, as it moves through the four VCP namespaces. Each step is a typed envelope on the bus; nothing here is a side call. The sequence below is the design-of-record path — the executing runtime is in private beta, not a shipped endpoint.

01
create_purchase_mandate
delegate
02
search
commerce
03
rank_offers
platform
04
negotiate
commerce · propose / counter / accept
05
verify + certify
platform
06
approve_purchase
delegate · human gate
07
authorize + settle
platform · psp
08
dispatch
commerce
09
dispute (optional)
platform
Mandate
consumerdelegate.create_purchase_mandatebuyerhuman delegates authority + budget
Discovery — routed through the platform
buyercommerce.searchplatform:aggregator
platform:aggregatorplatform.rank_offersbuyerranks across every merchant
Negotiate — agent ↔ agent, peer to peer
buyercommerce.propose_offer · counter_offer · accept_offermerchantprice moves inside a signed range
Verify the match
buyercommerce.accept_offerplatform:aggregator
platform:aggregatorplatform.create_match_certificatebuyerconstraint_fit · claim_grounding · inventory · reputation
Settle — only the PSP moves money
buyerplatform.settle_paymentplatform:psp
platform:pspworld.create_order · reserve_inventory · update_ledgerworldatomic, audited write
Fulfill
merchantcommerce.dispatchbuyermerchant inventory write is brokered, never direct

The router enforces a partition table at the wire — wire-level role authentication, not LLM self-discipline. Private-utility memory (max_budget, floor_price) never appears in any payload.

Mandate: delegating buyer authority

The journey opens with delegate.create_purchase_mandate, sent from consumer:persona to buyer:intent. It carries a PurchaseMandate: the natural-language goal, hard constraints (budget, delivery window, must-haves), soft preferences, and an authority block that bounds what the agent may do without asking. This envelope opens the session_id that the entire buyer journey nests inside. Money in the mandate, like everywhere in VCP, is integer minor units — a $400 budget is 40000.

Search and rank: finding candidates

buyer:discovery emits commerce.search to platform:aggregator. The buyer never reads merchant inventory directly; it discovers candidates through the platform and VCP evidence. The aggregator responds with platform.rank_offers, a ranked candidate list. Ranking is a platform-only execution action — a buyer can receive a ranking but can never mint one, which keeps the ordering attributable to a known policy version.

Negotiation: one session, nested rounds

Negotiation is a commerce.* exchange between buyer:negotiation and merchant:pricing. It opens with commerce.propose_offer carrying a GroundedOffer, then commerce.counter_offer rounds bounce back and forth, each linked to the previous via in_reply_to so the whole back-and-forth threads inside a single session. It terminates in commerce.accept_offer(or a reject). Throughout, neither side's private-utility values — the buyer's walk-away price, the merchant's floor — may appear in any payload.

A representative envelope mid-flow, the moment a buyer accepts a counter:

{
  "protocol": "vcp",
  "version": "1.0",
  "msg_id": "msg_0d41",
  "ts": "2026-05-15T12:04:18Z",
  "from": "buyer:negotiation",
  "to": "merchant:pricing",
  "session_id": "sess_abc",
  "in_reply_to": "msg_0c90",
  "idempotency_key": "idem_accept_0d41",
  "action": {
    "kind": "commerce.accept_offer",
    "payload": { "offer_id": "offer_7f2" }
  }
}

Verification and the match certificate

Acceptance does not move money. First the platform verifies. platform:aggregator runs platform.verify_claims — checking the GroundedOffer's claims against the merchant's permitted-claims set — then issues platform.create_match_certificate. A MatchCertificateattests that this mandate matched this offer truthfully under a named verification policy at a given time: constraint fit, claim grounding, inventory availability, reputation threshold. This certificate, not the merchant's cart, is what settlement will later consume.

Why it matters
The match certificate is VCP's novel object. It is the bridge between the buyer's mandate and verified execution: settlement depends on a platform-attested certificate rather than only a merchant-authoritative cart, so a merchant cannot get paid for an offer the platform never verified.

The human authority gate

Before payment, the deal passes through the human-in-the-loop gate. buyer:authorization presents the certified match to the principal, who replies with delegate.approve_purchase (or delegate.reject_purchase) referencing the cert_id. This is the answer to “will my agent spend without asking?”

The gate is governed by two fields in the mandate's authority block. can_buy_without_confirmation decides whether the gate can be skipped at all, and max_spend_without_confirmation sets the ceiling under which it may be. A deal whose total sits under the ceiling — and only such a deal — can settle autonomously; anything above it stops and waits for an explicit human approval envelope.

Settlement: the atomic world write

Only platform:psp settles. It runs platform.authorize_payment against the certificate and payment instrument, then platform.settle_payment, which is the one place the world actually changes: world.create_order, world.reserve_inventory, and world.update_ledger happen inside a single governed transaction. The runtime materializes the whole thing as one TransactionStateDiff with atomicity and idempotency invariants recorded. No buyer and no merchant role appears in this write — money moves through the PSP or not at all.

Dispatch and the optional dispute path

With the order on the books, merchant:fulfillment emits commerce.dispatch, applying a signed −qty delta to inventory and transitioning the order to shipped. For most deals the lifecycle ends here. When it does not, the buyer has scoped rights:

  • commerce.request_returnbuyer:authorization asks merchant:support for a return; it stays inside the same buyer session.
  • platform.open_dispute — escalates to platform:adjudicator in a fresh session; the adjudicator may request_evidence from either side.
  • platform.rule_dispute — the adjudicator rules, optionally triggering a refund and a reputation update. No merchant ever rules its own dispute.

How a single step actually runs

Each envelope in the flow above is produced by one agent turn. A turn is one inbound envelope driving a bounded internal loop — exactly one inference call per step — that terminates in at most one outbound envelope. The model emits structured decisions the agent acts on:

  • load_skill — pull a full SKILL.md into context for this turn only.
  • memory_update— write to the agent's private typed memory.
  • emit_envelope — construct and send the single outbound envelope, after outbound invariant checks.
  • no_reply — end the turn without speaking.

The loop is internal only. To get information from another participant, an agent must emit an envelope and wait for the runtime to deliver the response as a fresh turn — which is exactly why the lifecycle reads as a chain of envelopes rather than a call stack. For the namespaces and roles those envelopes ride on, see Core concepts.