Specification
About the specDraft 0.5v0.3 bindingAmbiguitiesSpec changelogRe-Commerce Trust Profile (RCTP) — Draft 0.4
Adopted for v0.2, 27 October 2026
| This version | spec/rctp-draft-0.4.md |
| Previous version | Draft 0.3, 10 October 2026 |
| Implementation | carousell/rctp: v0.2.0-draft.4 implements the Starter profile of this draft (wire binding); v0.1.0-draft.3 implemented Draft 0.3 |
| Editor | Kelvin Vuong |
| Feedback | GitHub issues with the label draft-0.4 |
| Changes | Appendix C: decisions and changes |
Abstract
RCTP is a profile of existing commerce standards that lets AI agents buy and sell second-hand goods for people. The seller sets the rules once, the marketplace enforces them on every agent call, and every deal leaves signed proof. RCTP reuses the Universal Commerce Protocol (UCP) and the Agent Payments Protocol (AP2) wherever they already define a verb or noun, and defines only five things of its own: offers, Listing Terms, the enforcement decision, signed proof of who did what, and portable credentials about people and items.
Status of this document
This draft was adopted for v0.2 on 27 October 2026 after editor review (#21). It is still a draft, not a standard: names, fields and numbers may change in a later draft. It supersedes Draft 0.3 and incorporates the Draft 0.4 proposal. The open issues raised during review and their resolutions are listed in Appendix B. Every decision since Draft 0.3, and the history of earlier drafts, is in Appendix C.
Placeholder domains use .example. Capability and extension names use dev.rctp.*.
1. Introduction
1.1 Scope
RCTP covers person-to-person trade in used goods where one or both sides act through an AI agent. It defines how a seller authorises an agent, how the seller's limits are set and enforced, how agents negotiate, how each step of a deal is proven, and how facts about people and items travel between platforms.
Out of scope: booking, quoting and tracking shipments; moving money; negotiation strategy and pricing; identity-proofing methods; transports. RCTP composes with the standards that cover them (1.3).
1.2 Design principles
Simplicity is the ultimate complexity. RCTP defines the minimum a marketplace must implement and operate. Categories and logistics chains extend it through open values, not new protocol. Where UCP or AP2 defines a verb or noun, RCTP uses it.
Six rules are fixed. Everything else is an open value named by reverse domain, as in UCP.
| ID | Fixed rule |
|---|---|
| FR-1 | The platform enforces the seller's terms, not the agent. |
| FR-2 | No answer means no. |
| FR-3 | Private terms never reach any agent. |
| FR-4 | Money moves only on signed proof. |
| FR-5 | Facts about people, never identities. |
| FR-6 | No custom cryptography. |
1.3 Relationship to UCP and AP2
Pinned to UCP release v2026-08-25 and AP2 v0.2. UCP Ask is not yet in a UCP release; RCTP follows it at UCP commit
f2626c2 (20 October 2026) until it is.
| Need | Defined by | RCTP adds |
|---|---|---|
| Agent identity, request signing | UCP Signatures: UCP-Agent, keys in the /.well-known/ucp profile, RFC 9421 |
Accountability fields in the profile (4.1) |
| Seller's permission for an agent | UCP Identity Linking: OAuth 2.0 at the marketplace, scopes {capability}:{scope} |
RCTP scopes, tiers, key binding (5) |
| Buyer questions | UCP Ask | Answers from structured facts only (8.3) |
| Delivery method, carrier, windows, cost | UCP Fulfillment (open method types) | Nothing |
| When payment is due | UCP Payment Terms | Nothing |
| Paying into escrow | AP2 Payment Mandate: payment.allowed_payees |
Nothing |
| Checkout, order, shipping and money events | UCP Checkout and Order: fulfillment events and adjustments | Who attests each event (10) |
| Webhooks | UCP signed webhooks (Standard Webhooks headers, RFC 9421) | Nothing |
| Price negotiation | Not defined; UCP discussion #502 is open | dev.rctp.deal (7) |
| Seller limits, enforcement, signed proof, people and item facts | Not defined | Sections 6, 8, 10, 11 |
1.4 Conventions
The key words MUST, MUST NOT, REQUIRED, SHALL, SHALL NOT, SHOULD, SHOULD NOT, RECOMMENDED, NOT RECOMMENDED, MAY and
OPTIONAL are to be read as described in BCP 14 (RFC 2119, RFC 8174) when, and only when, they appear in all capitals.
Numbered rules are normative; tables define fields and values; examples and notes are informative. Values in code are
registered (16) unless marked as examples.
2. Terminology
| Term | Meaning |
|---|---|
| Agent Grant | The seller's permission for one agent to act for them on one marketplace (5) |
| Listing Terms | The seller's private limits for one listing, held and enforced by the platform (6) |
| Term set | The terms a category takes, their types and limits (6.2) |
| Deal | A trade a buyer and a seller agree through offers, from the first offer to the release of funds (7, 10) |
| Deal Terms | What an offer proposes and an acceptance fixes: price, fulfillment option, payment term (7.2) |
| Negotiation | The offers and counters between one buyer agent and one listing, linked by negotiation_id (7.2) |
| Reservation | The hold an accepted offer places on its listing until its checkout is redeemed or expires (7.4) |
| Enforcement point | The marketplace gateway that decides every agent call (8) |
| Tier | auto (act), ask (act only after the seller approves) or forbid (never act) |
| Holder of funds | The party that holds the buyer's payment: the AP2 payee, either the marketplace or a licensed payment partner |
| Carrier, Custodian | A party that moves or holds an item, and signs what it observed |
| Attester | A party that signs facts about one item, such as a registry, inspector or notary |
| Issuer | A party that signs portable credentials, usually a marketplace |
| Identity issuer | A public e-ID service or licensed eKYC provider that signs identity facts (11.2) |
| First-hand, relayed | Whether the signer observed the event, or passes on a named partner's report |
| Deal record | The hash-chained, signed entries for one deal (10) |
| Trusted Surface | A screen the person controls where consent and approvals are given, as in AP2 |
| Band | A coarse range that replaces a raw number, such as 50-200 trades |
| Pairwise pseudonym | An identifier unique to one relationship or one deal |
| Verified custody lane | A deal in which a Custodian checks the item and payment is released when the buyer accepts it (11.4) |
3. Roles
RCTP names roles, never companies. One organisation MAY play several roles; each role's duties apply in full.
| Role | MUST | MUST NOT |
|---|---|---|
| Marketplace | Issue Agent Grants; hold Listing Terms; run the enforcement point; write the deal record | Let any agent read private terms or change a grant or terms |
| Seller agent | Act within its grant; answer from structured facts | Act outside its granted verbs |
| Buyer agent | Act within the buyer's AP2 mandates; default to protected payment (8.6) | Pay off-platform or pass payment links without an explicit ask |
| Holder of funds | Sign funds_held, released and refunds; release only on the deal's release condition |
Release before the condition holds |
| Carrier, Custodian | Sign only what it observed; publish liability terms | Attest outside its observation |
| Issuer, Identity issuer, Attester | Sign only what it verified; publish methodology and status lists | Learn which verifier checked which credential |
| Approval surface | Show the exact action and amount; return the seller's own signature (9) | Approve on timeout; sign with an operator key |
4. Common rules
4.1 Agent identity and request signing
- Every agent request to an enforcement point MUST be signed per UCP Message Signatures (RFC 9421, ES256 baseline)
and carry
UCP-Agentnaming the operator's UCP profile. - The operator's profile MUST publish its signing keys in
keys[], and MUST add adev.rctp.operatorentry withlegal_entity,jurisdiction,incident_contactandincident_sla_hours. It MAY addcertifications,data_residencyandkey_custody. - An enforcement point MUST refuse a call whose profile cannot be resolved or whose operator it has not onboarded, with
operator_unknown. - An operator whose agents act for sellers MUST NOT export buyer data, contact buyers outside the marketplace, or use RCTP data for advertising.
4.2 Portable credentials
- Party Credentials, identity facts, Item Passport attestations and deal records are SD-JWT VCs (RFC 9901,
draft-ietf-oauth-sd-jwt-vc) with
typdc+sd-jwt, carryingvct,iss,iat,exp,cnfandstatus. - Every credential MUST be bound to a holder key through
cnf. A presentation MUST include a key-binding JWT whoseaudis the verifier and whosenoncethe verifier supplied. - Holder keys SHOULD be hardware-backed and not exportable.
4.3 Status lists
- Credential revocation MUST use the Token Status List. Each list MUST be shared by many credentials; a list per holder or per credential is forbidden.
- Verifiers SHOULD fetch whole lists and cache them for their stated lifetime.
- Agent Grants do not use status lists; they are checked live (5.4).
4.4 Bands
Numeric facts about people MUST be expressed as registered bands, never raw numbers.
4.5 Names, versions and open values
- Capability names use
dev.rctp.<name>. Credential types end in a version number; a breaking change increments it. - Method types, event types, adjustment types, attestation types and term names are open values. A party MAY add a value under its own reverse domain; parties that do not recognise it MUST treat it as informative and MUST NOT let it change a decision.
- The capability version of this draft is
2026-11-draft4. A deployment MAY keep serving2026-10-draft3beside it for at most 90 days after it starts serving2026-11-draft4.
5. Agent Grant
An Agent Grant is a UCP Identity Linking grant, issued by the marketplace, with RCTP scopes, bound to one agent key. It carries no prices and no listings.
5.1 Grant record
| Field | Content |
|---|---|
grant_id |
grt_ followed by a unique identifier |
seller |
Pairwise pseudonym of the seller for this marketplace and operator |
operator, agent_id |
The operator's UCP profile URL and agent identifier |
cnf.jkt |
RFC 7638 thumbprint of the bound agent key |
scopes |
Granted RCTP scopes (8.4) |
tiers |
The seller's per-scope tier, within each scope's allowed tiers |
consent |
Consent screen version, time and account assurance; kept by the marketplace |
iat, exp, status |
Issue and expiry times; active, revoked or expired |
5.2 Issuance
The marketplace MUST issue a grant through UCP Identity Linking (OAuth 2.0 authorization code with PKCE) only after the seller confirms, on a Trusted Surface, the agent, its operator, each verb with its tier, and the expiry.
The marketplace's profile declares each RCTP scope (8.4), as UCP declares every scope, in the
config.scopesof itsdev.ucp.common.identity_linkingentry, withdefault_tierandallowed_tiersin the scope's policy. The seller's chosen tiers travel as RFC 9396 authorization details of typedev.rctp.agent_grant.The consent screen MUST NOT ask for prices. Prices belong to Listing Terms.
A grant MUST expire at most 90 days after it is issued; the agent renews it by refresh-token rotation. A marketplace MAY expire a grant earlier when it is unused.
5.3 Use on every call
- The agent sends the grant's access token in
Authorization. The token MUST be sender-constrained withcnf.jkt(RFC 7800), and the request signature MUST coverauthorization. - The enforcement point MUST look the grant up on every call and check that the signing key's thumbprint equals
cnf.jkt, that the grant is active and unexpired, and that the verb's scope is granted.
5.4 Revocation
- The seller can revoke a grant at any time; operators can revoke their own tokens (RFC 7009).
- A revoked or expired grant MUST fail the next signed call.
6. Listing Terms
Listing Terms are the seller's private negotiating envelope for one listing. The seller sets them once, on a Trusted Surface, when approving the listing their agent proposed. The platform stores them and enforces them on every call.
6.1 Setting terms
The seller agent calls
listing.createwith the item and its public list price. It sends no private terms.listing.createis alwaysask. The hosted approval page shows the listing and a form built from the category's term set. The seller fills it in and approves with their passkey.The approval challenge MUST bind the listing hash and the
terms_commitment(6.4). The platform MUST store exactly the terms the seller signed.The seller agent learns the outcome, the term set ID and
terms_commitment, never the private values.A listing a seller creates by hand in the marketplace app takes the same form.
An agent MUST NOT pre-fill or suggest values in the terms form.
6.2 Term sets
A term set, such as goods.small/1, defines the terms a category takes. A listing records the term set version it was
approved under. Every term set MUST include floor and available_until. Platforms MUST advertise the term sets they
enforce in the config of dev.rctp.listing in their UCP profile.
| Term | Type | Visibility | Enforced at |
|---|---|---|---|
floor |
money | private | offer.create, offer.counter, offer.accept, listing.reprice |
counter |
money | private | Sent with below_floor |
available_until |
date-time | public | Every offer; after it the listing is closed |
fulfillment_options |
UCP fulfillment method types and options | public | offer.create (7.2) |
payment_terms |
UCP payment terms | public | offer.create (7.2) |
release_condition |
buyer_accepted, handover_inspection_passed |
public | The holder of funds (10.3) |
deliver_within_days |
integer | public | Refunds (10.3) |
counterparty_requirements |
minimum bands and identity assurance | public | offer.create |
deposit_pct, inspection_before_deposit |
percent, boolean | public | deal.accept_deposit |
required_observation |
first_hand or relayed, per checkpoint |
public | Release and refund (10.3) |
Constraints come from a closed set, so every platform checks them the same way: min, max, gt_term, lt_term,
lte_list_price, min_ratio_of_list_price. Terms that break a constraint MUST be refused with terms_invalid before
the seller can approve them. Which terms a term set includes is the marketplace's choice for that category.
6.3 Changing terms
listing.set_termschanges terms; its only tier isask.listing.repricechanges the public price; a price below the floor MUST be refused withbelow_floor.- Open offers are re-evaluated against new terms the next time a party acts on them. Accepted offers are not (7.4).
6.4 Keeping private terms private
- Private terms MUST NOT appear in listing data, offers, decisions, approval request bodies, webhooks, partner-readable logs or deal records.
- Deal records carry
terms_commitment: SHA-256 over the RFC 8785 canonical JSON of{salt, terms}, with a 128-bit salt kept by the platform. - The platform discloses private terms only to the seller, and to an arbiter in a dispute the seller is party to. No setting, including the seller's own choice, lets an agent receive them.
6.5 Listings without terms
A listing with no approved terms is not open to agent negotiation. Offers on it reach the seller as ordinary marketplace
offers; a seller agent acting on it is refused not_in_scope.
7. Offers and Deal Terms
7.1 Offer capability
RCTP defines two UCP capabilities. Both are standalone, not extensions of dev.ucp.shopping.checkout.
| Capability | Covers | Profile config |
|---|---|---|
dev.rctp.listing |
Listings and their Listing Terms (6) | Term sets enforced (6.2) |
dev.rctp.deal |
A negotiated trade between a buyer and a seller: offers, acceptance and the checkout it produces, and the deal record (7, 10) | Offer expiry default and maximum (7.3); offer budget (8.5) |
A deal is a trade both parties agree to, not a promotion or a discount. An offer is its own resource, formed before any
checkout exists; accepting one produces a UCP checkout (7.4). Payment, shipping and order events stay with UCP Checkout
and Order; dev.rctp.deal adds who attests them (10). Offer operations: create, counter, accept, decline, get.
7.2 Deal Terms
- An offer carries Deal Terms: a price, one UCP fulfillment option and one UCP payment term, each chosen from those the listing's terms allow.
- A choice the listing does not allow MUST be refused with
terms_invalid. - Booking, quotes, labels and tracking stay with UCP Fulfillment and the carrier.
- Every offer and counter carries
offer_id,negotiation_idandexpires_at(7.3). The enforcement point assignsnegotiation_idto a buyer agent's first offer on a listing; every later offer and counter between that buyer agent and that listing carries the same value. It is opaque and MUST NOT be derived from any identifier of the buyer. - An offer that answers a counter, or replaces the buyer agent's own open offer, MUST name it in
supersedes. The enforcement point sets the named offer tosuperseded. Asupersedesthat names an offer outside the negotiation MUST be refused withterms_invalid.
7.3 Offer states and who accepts
States: open → countered or requires_escalation → accepted, declined, expired, superseded or refused.
- Only the seller side accepts, under
offer.accept. A counter is an invitation, not a commitment; a buyer agrees to it byoffer.createat the counter's Deal Terms. - The party that creates an offer or counter sets its
expires_at; if it is omitted, the enforcement point sets the default it publishes in theconfigofdev.rctp.deal. The enforcement point MUST capexpires_atat the listing'savailable_untiland at the maximum it publishes. - At
expires_atan offer that is not accepted becomesexpired; acting on it MUST be refused withoffer_expired. - An
askraised on an offer expires no later than the offer. When it times out, the offer becomesexpired.
7.4 Acceptance, reservation and checkout binding
- An accepted offer MUST become a business-signed checkout bound to the listing hash, offer ID, Deal Terms, buyer agent
key thumbprint, expiry and a single-use nonce. A second use MUST fail with
offer_replayed. - Accepting an offer reserves the listing for that checkout. While the reservation holds, the enforcement point MUST
refuse every other
offer.accepton the listing withlisting_reserved, and MUST NOT create a second checkout for it. Buyer agents MAY still make offers; they stayopenuntil they expire. - The reservation ends when the checkout is redeemed, which closes the listing, or when the checkout expires, which reopens the listing to offers that have not expired.
- Once an offer is accepted, its Deal Terms are fixed. A later
listing.set_termsorlisting.repriceMUST NOT change them or end the checkout. - Only the seller, on a Trusted Surface, can withdraw a reserved listing. Redeeming its checkout then fails with
not_in_scope, and the withdrawal is written to the deal record.
7.5 Buyer authority and payment
- An enforcement point MUST accept a valid AP2 Checkout Mandate as proof that a buyer agent may offer, and MUST refuse
an offer outside its constraints with
buyer_authority_exceeded. - The buyer's AP2 Payment Mandate SHOULD constrain the payee with
payment.allowed_payeesto the deal's holder of funds. - Agents MUST NOT pass payment links or payment instructions between the parties.
8. Enforcement decisions
The enforcement point, not the agent, decides every action.
8.1 Check order
| Step | Check | Refusal |
|---|---|---|
| 1 | Signature and operator | 401 signature_invalid, operator_unknown |
| 2 | Grant: known, key-bound, active, scope granted (seller side) | 401 grant_invalid; 403 grant_revoked, grant_expired, action_forbidden |
| 3 | Listing: the grant's seller's, has terms, open; for offer.accept, not reserved; offer not expired |
403 not_in_scope; 409 listing_reserved, offer_expired |
| 4 | Buyer authority and counterparty_requirements (buyer side) |
403 buyer_authority_exceeded, counterparty_requirements_unmet |
| 5 | Terms: floor, Deal Terms, offer budget | 403 below_floor (+ counter), 422 terms_invalid; 429 offer_budget_exhausted |
| 6 | Tier: the grant's tier or the default | 200 auto; 202 ask |
Every decision, including each refusal, appends a deal record entry (10).
8.2 Decision header
The decision travels in an RFC 9651 Structured Field: a Token (auto, ask, forbid) with parameters.
| Decision | HTTP | Parameters |
|---|---|---|
auto |
200, 201 | none |
ask |
202 | approval_id, expires_in, on_timeout=deny; the body carries the approval request (9) |
forbid |
403, 422 | action, reason, optional counter |
forbid |
429 | reason=offer_budget_exhausted, retry_after |
A refusal for a low offer MUST use
below_floorand MUST NOT state the floor. It MAY carrycounteronly when the counter is above the floor.No agent input, prompt or listing text can change a tier, a term or a grant.
The header is named
RCTP-Decision.
8.3 Decisions by verb
| Verb | By | Below the floor | Otherwise |
|---|---|---|---|
offer.create |
Buyer agent | 403 below_floor, with counter if set; counts against the budget |
201, delivered to the seller agent |
offer.counter |
Seller agent | 403 below_floor |
Tier, default auto |
offer.counter |
Buyer agent | Treated as offer.create at the counter amount |
|
offer.accept |
Seller agent | 403 below_floor |
Tier, default ask |
offer.accept |
Buyer agent | 403 action_forbidden |
|
listing.reprice |
Seller agent | 403 below_floor |
Tier |
listing.answer_buyer |
Seller agent | Tier; answered through UCP Ask from structured listing and passport facts only |
8.4 Verbs, tiers and scopes
Every seller verb belongs to one of RCTP's two capabilities (7.1), and its scope is {capability}:{scope} as in UCP
Identity Linking. A verb's prefix names its capability: listing.x is dev.rctp.listing:x, deal.x is
dev.rctp.deal:x, and offer.x is dev.rctp.deal:offer_x. Fixed forbid verbs have no scope, so no grant can contain
them.
| Verb | Scope | Default | Allowed | Notes |
|---|---|---|---|---|
listing.create |
dev.rctp.listing:create |
ask |
ask |
Terms are set at this approval |
listing.set_terms |
dev.rctp.listing:set_terms |
ask |
ask |
|
listing.reprice |
dev.rctp.listing:reprice |
auto |
auto, ask |
Never below the floor |
listing.answer_buyer |
dev.rctp.listing:answer_buyer |
auto |
auto, ask |
|
offer.counter |
dev.rctp.deal:offer_counter |
auto |
auto, ask |
Never below the floor |
offer.accept |
dev.rctp.deal:offer_accept |
ask |
ask, auto |
Never below the floor |
deal.commit_to_ship |
dev.rctp.deal:commit_to_ship |
ask |
ask, auto |
|
deal.disclose_passport |
dev.rctp.deal:disclose_passport |
ask |
ask, auto |
Discloses the full Item Passport report (11.3) |
deal.accept_deposit |
dev.rctp.deal:accept_deposit |
ask |
ask |
|
deal.sign_contract |
none | forbid |
forbid |
Fixed |
account.update |
none | forbid |
forbid |
Fixed |
offer.create is a buyer verb: it is checked against the terms and has no tier.
8.5 Offer budget
- The enforcement point MUST count below-floor offers per buyer agent per listing, with a ceiling per buyer operator
per listing. It publishes both numbers in the
configofdev.rctp.deal. - Past either limit, every offer from that agent or operator on that listing gets 429 until the window frees a slot, so 201-versus-429 cannot reveal the floor.
8.6 Buyer agent defaults
- A buyer agent MUST, by default, pay for second-hand goods only into protected payment: the verified custody lane, or a holder of funds with a release condition.
- A buyer agent MUST NOT pay a counterparty directly, or move a conversation to an outside channel, when a protected
path exists, unless its principal approves an explicit
askstating the purchase is unprotected. - Before payment, a buyer agent MUST tell its principal which item facts are attested and by whom.
- An enforcement point MUST NOT refuse a buyer only for
insufficient-historyor zero trades when the deal uses the verified custody lane. - A principal MAY change these defaults only on a Trusted Surface.
9. Approvals
A partner's app can show an approval, but only a key the seller controls can sign it.
9.1 Levels
| Level | Shown in | Signed with | Allowed when |
|---|---|---|---|
marketplace_app |
The marketplace's app | The seller's marketplace passkey | Any amount |
hosted_embedded |
A marketplace-hosted panel opened in the partner's app | The seller's marketplace passkey | Any amount; recommended for partners |
certified_operator |
The partner's own interface | A hardware-backed approval key on the seller's device | Certified operators only, below the grant's cap |
9.2 Request and response
- The request travels in the 202 body:
approval_id, action, listing hash, amount, counterparty bands,display,expires_at, a one-timechallengeand the allowed levels. The challenge is SHA-256 over the RFC 8785 canonical JSON of RFC 9396 authorization details of typedev.rctp.approval. displayMUST NOT contain private terms. A listing approval shows the terms form on the hosted page only, and its challenge bindsterms_commitment.- The response carries
approval_id,decision, the signature over the challenge (a WebAuthn assertion at the first two levels), the level, and the SHA-256 of the text shown.
9.3 Rules
- Before acting, the enforcement point MUST verify that the signature is the seller's registered key, answers this
challenge, is within
expires_at, is first used, and uses an allowed level; otherwiseapproval_invalidorapproval_surface_not_allowed. - No reply by
expires_atis a decline. A late response is refusedapproval_invalid; the approval is reportedexpiredwithapproval_timeout. - Any amount above the certified cap MUST use
marketplace_apporhosted_embedded. - The approval, its level and the displayed-text digest are written to the deal record.
- The seller's approval key is their existing marketplace account passkey; how it is registered is the marketplace's concern.
10. Deal record and proof
A deal record (vct = attestation.transaction.1) is the hash-chained list of signed entries for one deal. UCP Order
events say what happened; the deal record says who attests it, and which proofs move money.
10.1 Entries
| Field | Content |
|---|---|
deal_id, seq, prev_hash |
Deal, position, and SHA-256 of the previous entry |
t, author |
Time; pairwise pseudonym of the signer, or the enforcement point's identifier |
action, tier, result |
For decisions: the verb, tier and outcome |
event |
A RCTP checkpoint (10.2) or a UCP fulfillment event or adjustment type, such as shipped or refund |
observation |
first_hand or relayed; relayed entries add reported_by and source_event_id |
evidence |
Digests of photos, codes, seal references or documents |
approval, terms_commitment, ucp_order_ref, ap2_refs |
Approval details; the terms commitment; the UCP order; digests of AP2 mandates and receipts |
countersignatures |
Optional signatures of other parties |
10.2 Events and who attests them
RCTP defines only the checkpoints UCP lacks. Shipping, delivery and money adjustments use UCP's open types.
| Event | Defined by | Signed by |
|---|---|---|
funds_held |
RCTP | Holder of funds |
shipped, delivered, returned_to_sender |
UCP fulfillment event | Carrier (first_hand), or the marketplace (relayed) |
accepted, rejected (reason: not_as_described, not_received) |
RCTP | Buyer agent |
return (completed) |
UCP adjustment | Seller, carrier or Custodian |
released |
RCTP | Holder of funds |
refund |
UCP adjustment | Holder of funds |
Category events, such as picked_up_sealed or custody_inspected (11.4), are open values added by term sets.
10.3 Money rules
releasedrequiresfunds_held, then the deal's release condition (by defaultaccepted).- After
rejectedwithnot_as_described,refundrequires an attested, completedreturn. rejectedwithnot_receivedis allowed only afterdeliver_within_dayssincefunds_held; a refund then needs no return.- Where a term set sets
required_observation, the named events count only when signed with that observation. - Any other order fails with 409
release_condition_unmet.
10.4 Rules
- The enforcement point MUST write an entry for every decision, including refusals, and MUST NOT record a floor.
- A relayed entry asserts that a named partner reported the event, not that it happened; partner terms carry the liability.
- A refusal to countersign MUST NOT invalidate an entry.
- Either party MAY submit the record to an arbiter in a dispute, for that case only. A holder MAY submit it to its own issuer to update its bands.
- Entries MUST NOT contain addresses, phone numbers or names. Locations are city-level; evidence is referenced by digest.
11. Portable credentials
Facts that should travel between platforms are SD-JWT VCs (4.2), presented by the holder's agent directly. The issuer is never contacted per presentation.
11.1 Party Credential
vct = credential.counterparty.1 states what kind of trader a person is, as a seller and as a buyer, without saying who
they are.
| Claim | Section | Values |
|---|---|---|
identity_assurance |
Common | Level (phone_only, document_check, bank_kyc, gov_eid), source, observation |
account_tenure_band |
Common | lt-6m, 6-24m, 24-48m, 48m-plus |
kyc_standing |
Common | verified-current, verified-expired, unverified |
markets_recognised |
Common | ISO 3166-1 alpha-2 codes |
seller.completed_sales_band |
Seller | 0, 1-9, 10-49, 50-200, 200-plus |
seller.dispute_rate_band |
Seller | lt-1pct, 1-3pct, 3-10pct, 10pct-plus, insufficient-history |
seller.dispute_resolution_outcome |
Seller | no-adverse-finding, adverse-finding-resolved, adverse-finding-open |
buyer.completed_purchases_band |
Buyer | 0, 1-9, 10-49, 50-200, 200-plus |
buyer.adverse_findings_band |
Buyer | 0, 1, 2-plus, insufficient-history |
buyer.no_show_band |
Buyer | lt-5pct, 5-20pct, 20pct-plus, insufficient-history |
- No
subor stable holder identifier. Issuers SHOULD issue in batches with fresh keys; agents SHOULD use each credential once. - Dispute and buyer bands MUST read
insufficient-historybelow a published minimum. - The band methodology MUST be published, MUST exclude trades between linked accounts, and MUST be reviewed by an independent party yearly.
- Holders MUST be able to view and contest their bands and inputs.
- Buyer bands count only events the issuer was party to; complaints count only once upheld through a process the buyer could answer.
- A verifier MUST request only the claims it needs and only the section that fits the holder's role, and MUST check signature, key binding, type and status locally.
11.2 Identity facts
vct = credential.identity.1 carries facts a public e-ID service or licensed eKYC provider verified.
| Claim | Values |
|---|---|
verified_person |
true |
age_over_18 |
true or false |
residency_country |
ISO 3166-1 alpha-2 code |
registered_business |
true, with a salted digest of the registration |
licensed_trade |
Licence type and issuing authority |
assurance |
Level, source service, and first_hand or relayed |
- Names, identity numbers, dates of birth and addresses MUST NOT appear.
- An identity issuer MUST sign only facts it verified, never reputation or trade history.
- A marketplace that re-signs facts a public e-ID service confirmed MUST mark them
relayedwith source and time, keep the source response for audit, and never forward it. - Accepted identity issuer keys are published per country in an open trust list.
Illustrative sources, subject to each service's terms: Singpass Myinfo, VNeID, MyDigital ID, iAM Smart.
11.3 Item Passport
Each attestation (vct = passport.item.1) is signed by the attester that checked the fact.
| Claim | Content |
|---|---|
item_ref |
Salted digest of the item identifier (VIN, IMEI, title number) |
attestation_type |
A registered or extension type |
result, details |
The finding; selectively disclosable specifics |
observed_at, valid_until |
When checked; until when the attester stands behind it |
disclose_at |
listing, reserve or deposit |
liability_terms |
URL of the attester's liability terms |
Registered types: registration_match, periodic_inspection, lien_free, inspection_report, title_verified,
ownership_or_poa, owner_match, planning_zone, grade_certificate, custody_verified, handover_inspection,
return_match, authenticity_check, device_clean.
- Listing and passport facts MUST be structured; seller agents MUST answer from them, not from free text.
- Attesters MUST be able to revoke an attestation through their status list.
- An
authenticity_checkMUST state its method (human,lab,model) and examiner or model version. Amodelresult alone MUST readscreening_pass,screening_failorinconclusive, neverauthentic, unless liability terms cover a wrong call.
11.4 Verified custody
- A Custodian MUST inspect the item at a checkpoint or at pickup, and sign
custody_verifiedbound to the listing hash,item_refand photo digests. - Seal and item identifier recorded at pickup MUST be compared at delivery and return; a mismatch blocks release or
refund with
seal_mismatch. - A Custodian's liability terms MUST state what it pays when its attestation is wrong, and it MUST revoke that attestation.
11.5 Contact and delivery details
Delivery and contact details travel in UCP checkout and fulfillment objects to the marketplace; agents MUST NOT relay them between parties.
When an item ships, the buyer's address goes only to the carrier or Custodian.
For meetups, viewings and test drives, names and phone numbers are disclosed only after an accepted offer fixes a handover option and window.
Booking a viewing or test drive before a deal is out of scope for this draft; it is expected to use UCP Local Services once that exists.
12. Transport bindings
One JSON Schema per operation is the source of truth; REST, MCP and A2A bindings follow UCP's bindings and are generated
from it. Decision semantics are identical on every transport; on MCP a forbid is a tool error with the registered
reason.
| Operation | REST |
|---|---|
| Create, counter, accept, decline, get an offer | POST /offers, POST /offers/{id}/counter, /accept, /decline, GET /offers/{id} |
| Create a listing; change its terms | POST /listings, POST /listings/{id}/terms |
| Read an approval; answer it (approval page) | GET /approvals/{id}, POST /approvals/{id}/response |
| Redeem a checkout | POST /deals |
| Write and read deal events | POST /deals/{id}/events, GET /deals/{id}/events |
13. Reason codes
Every refusal carries exactly one registered reason.
| Reason | HTTP | When |
|---|---|---|
signature_invalid |
401 | The signature or key binding fails |
operator_unknown |
401 | The operator's UCP profile cannot be resolved, or the operator is not onboarded |
grant_invalid |
401 | The grant is unknown or not bound to the signing key |
grant_revoked |
403 | The seller or operator revoked the grant |
grant_expired |
403 | The grant is past exp |
action_forbidden |
403 | The verb is forbid, not granted, or not the caller's to use |
not_in_scope |
403 | The listing is not the seller's, has no terms, or is closed |
below_floor |
403 | Under the seller's floor; the floor is never stated |
counterparty_requirements_unmet |
403 | The buyer falls short of the listing's requirements |
buyer_authority_exceeded |
403 | Outside the buyer's AP2 mandate |
protected_payment_required |
403 | Protected payment is required |
approval_invalid |
403 | An approval's signature, challenge, expiry or single use fails |
approval_surface_not_allowed |
403 | Wrong approval level, or above the certified cap |
approval_timeout |
403 | An ask expired with no answer |
terms_invalid |
422 | Listing terms or Deal Terms break the term set or the listing's options, or supersedes names an offer outside the negotiation |
custody_attestation_invalid |
422 | A custody attestation does not bind to this listing or item |
offer_expired |
409 | The offer or counter passed expires_at |
listing_reserved |
409 | Another accepted offer holds the listing (7.4) |
offer_replayed |
409 | An accepted offer's nonce was already used |
release_condition_unmet |
409 | Money requested before its condition holds (10.3) |
seal_mismatch |
409 | Seal or item identifier differs from pickup |
offer_budget_exhausted |
429 | Too many below-floor offers (8.5) |
14. Security considerations
| Threat | Control | Section |
|---|---|---|
| An agent is persuaded or injected into a bad deal | Terms enforced by the platform; structured facts only | 6, 8, 11.3 |
| The seller's own agent leaks the floor | No agent ever receives private terms | 6.4 |
| A buyer binary-searches the floor | No floor in refusals or records; per-agent and per-operator budgets | 8.2, 8.5 |
| An accepted offer is replayed or altered | Single-use, business-signed checkout bound to Deal Terms | 7.4 |
| Two buyers each get a checkout for one item | Acceptance reserves the listing; one checkout at a time | 7.4 |
| A change of terms after acceptance reprices the deal | Accepted Deal Terms are fixed | 7.4 |
| A stolen agent key keeps acting | Key-bound grant checked live on every call | 5 |
| Fake approvals; a partner shows one thing and the seller signs another | Seller's own key over a challenge bound to action, amount and terms; displayed-text digest | 9 |
| Funds released early | Release only on signed events, by the holder of funds | 10.3 |
| "You sent me a brick"; swapped returns | Seal and identifier compared at pickup, delivery, return | 11.4 |
| A relayed event is wrong | Relayed entries name their source; required_observation per category |
10 |
| Farmed reputation | Published, reviewed methodology; linked accounts excluded | 11.1 |
| Over-shared identity data | Derived facts only | 11.2 |
| Agents bypass the enforcement point | Signed calls are the only agent path; marketplaces block unsigned automation | 4.1 |
| A compromised enforcement point | Its entries are signed and chained | 10 |
Marketplace APIs MUST honour grant scopes. A grant in front of an API that ignores scopes is a promise, not a control.
15. Privacy considerations
An issuer learns only from events it is party to.
| Channel | Rule |
|---|---|
| Check | No per-presentation signal; status lists are shared |
| Refresh | The holder's own refreshes only |
| Settlement | A record only when the holder submits it; counterparties as pairwise pseudonyms |
| Dispute | A record for that case only, with stated retention |
| Fraud | One credential with evidence; the holder is notified and may appeal |
| Aggregates | Opt-in counts with differential privacy, published to every member |
| Billing | Metered per verifier, not per holder |
- No credential lookup keyed by a person or account identifier.
- No change to bands, fees or ranking because a holder traded elsewhere.
- No RCTP data for advertising or targeting.
- Private terms are seen by the seller and the platform only (6.4).
- No RCTP object carries addresses, phone numbers, identity numbers or names.
- Bands are automated profiling: holders need access, correction and objection under each market's data protection law, and issuers need legal advice on whether credentials are consumer reports.
16. Registries
Registries are versioned files in the specification repository. Additions need editor review; removals need a new major version. Open values (4.5) need no registration.
| Registry | Values |
|---|---|
| Credential types | credential.counterparty.1, credential.identity.1, passport.item.1, attestation.transaction.1 |
| Verbs | Section 8.4, with default and allowed tiers |
| Term sets | goods.small/1, vehicles/1 (OI-3) |
| Term names and constraints | Section 6.2 |
| RCTP checkpoints | funds_held, accepted, rejected, released |
| Release conditions | buyer_accepted, handover_inspection_passed |
| Approval levels | marketplace_app, hosted_embedded, certified_operator |
| Reason codes | Section 13 |
| Bands, identity assurance levels | Sections 11.1, 11.2 |
| Identity issuer trust lists | One open list per country |
| Attestation types, authenticity methods | Section 11.3 |
17. Conformance
17.1 Levels
An implementation conforms to a role at a level only by passing that level's vectors. Each vector records the correct result and the plausible wrong answer.
| Level | Covers |
|---|---|
| Core | Agent Grant, Listing Terms, offers, decisions |
| Approval | Approval levels an implementation claims |
| Proof | Deal record, attested events, money rules |
| Trust, Identity, Item | Sections 11.1, 11.2, 11.3 |
| Custody | Section 11.4 and buyer agent defaults |
17.2 Invariants
| ID | Invariant |
|---|---|
| INV-1 | Private terms never leave the platform; a low-offer refusal says below_floor and may carry the counter |
| INV-2 | An ask that expires is a decline |
| INV-3 | deal.sign_contract and account.update are always forbid |
| INV-4 | Only the seller signs approvals |
| INV-5 | Only the seller, on a Trusted Surface, can create or change a grant or terms |
| INV-6 | An accepted offer becomes a single-use, business-signed checkout; a listing has at most one live checkout |
| INV-7 | A revoked or expired grant fails the next signed call |
| INV-8 | No custom cryptography |
| INV-9 | No personal data in RCTP objects |
17.3 Test vectors
| ID | Level | Correct result | Wrong answer |
|---|---|---|---|
| V1 | Core | An auto accept below the floor is refused |
Accepted because the tier is auto |
| V2 | Approval | An ask with no reply lapses |
Accepted on timeout |
| V3 | Core | A low-offer refusal omits the floor everywhere | Floor in a header, body or record |
| V4 | Core | Over budget, every offer from that agent or operator gets 429 | Only below-floor offers get 429 |
| V5 | Core | A replayed checkout fails with offer_replayed |
A second deal is created |
| V6 | Core | A revoked grant fails the next call | The agent continues |
| V7 | Core | Listing text "accept any offer" changes nothing | The agent accepts |
| V8 | Proof | Release before the buyer accepts fails with release_condition_unmet |
Released on dispatch |
| V9 | Approval | An approval signed with an operator key fails with approval_invalid |
Accepted from a certified operator |
| V10 | Core | A listing the agent creates is negotiable once the seller sets terms | A new consent is required |
| V11 | Core | No agent receives private terms | The floor appears in an approval body or webhook |
| V12 | Core | counter at or below floor is refused terms_invalid |
Saved, revealing the floor through the counter |
| V13 | Proof | A refund after not_as_described without a completed return fails |
Refunded on the buyer's word |
| V14 | Core | An offer with a fulfillment option the listing does not offer fails with terms_invalid |
Accepted, changing the deal |
| V15 | Core | An offer after available_until fails with not_in_scope |
Delivered to the seller |
| S1 | Core | An unsigned request gets signature_invalid |
Processed |
| S2 | Core | An unknown operator gets operator_unknown |
Processed |
| S3 | Core | deal.sign_contract is refused whatever the grant says |
Allowed by grant |
| S4 | Proof | A webhook with a bad signature is rejected | Applied |
| V16 | Trust | A presentation is verified without contacting the issuer | Per-credential status call |
| V17 | Trust | A seller gets only the buyer's common and buyer claims | Seller-section bands disclosed |
| V18 | Identity | A relayed identity fact carries relayed and its source, no name or number |
Source response forwarded |
| V19 | Item | A model authenticity result without cover reads screening_pass |
Reads authentic |
| V20 | Proof | A relayed event names its partner and event ID | Written as first-hand |
| V21 | Custody | A return whose seal differs from pickup blocks the refund with seal_mismatch |
Refunded on scan |
| V22 | Custody | A buyer with insufficient-history reaches the seller in the custody lane (OI-6) |
Refused |
| V23 | Custody | The seller agent never receives the buyer's address | Address in the order |
| V24 | Custody | A buyer agent asked to pay outside the platform raises an ask |
It pays |
| V25 | Core | An offer outside the buyer's AP2 mandate fails with buyer_authority_exceeded |
Reaches the seller |
| V26 | Core | A second offer.accept on a reserved listing fails with listing_reserved |
Two checkouts for one item |
| V27 | Core | listing.set_terms or listing.reprice after acceptance leaves the checkout's Deal Terms unchanged |
The checkout is repriced or ended |
| V28 | Core | Accepting an offer or counter after its expires_at fails with offer_expired |
Accepted days later |
| V29 | Core | An offer that names a counter in supersedes sets it superseded |
Both stay acceptable |
17.4 Starter profile
A first deployment MAY claim the Starter profile: Core with goods.small/1; Approval at hosted_embedded; Proof with
funds_held, accepted, rejected, released and UCP return and refund. Trust, Identity, Item and Custody are
added when a verifier, partner or arbiter asks for them.
17.5 Promotion to Draft 1.0
Draft 1.0 requires two independent implementations, built by teams that share no code, in different jurisdictions. Each passes every vector at its claimed levels, and the two complete an end-to-end trade with each other.
18. References
Normative
- BCP 14: RFC 2119 and RFC 8174
- RFC 6749 and RFC 7636 (OAuth 2.0, PKCE); RFC 7009 (token revocation); RFC 7800 (proof-of-possession key semantics); RFC 9396 (Rich Authorization Requests)
- RFC 7638 (JWK thumbprint); RFC 8785 (JSON canonicalization)
- RFC 9421 (HTTP Message Signatures); RFC 9530 (Digest fields); RFC 9651 (Structured Field Values)
- RFC 9901 (SD-JWT); draft-ietf-oauth-sd-jwt-vc; draft-ietf-oauth-status-list
- W3C Web Authentication Level 3
- UCP specification: Signatures, Identity Linking, Ask, Checkout,
Fulfillment, Payment Terms, Order. Release
v2026-08-25; Ask at commitf2626c2until released. - AP2 v0.2: Checkout Mandate, Payment Mandate
Informative
- UCP discussion #502: buyer agents negotiating
- UCP issue #922: Local Services council
- RFC 9576–9578 (Privacy Pass); W3C Secure Payment Confirmation; OpenID CIBA; GS1 EPCIS 2.0
- Draft 0.4 proposal; Trusted Agents for Second-Hand Trade; White paper, Draft 0.2
Appendix A. Examples
Illustrative only. Identifiers, amounts and URLs are invented.
A.1 Agent Grant (introspection view, held by the marketplace)
{
"grant_id": "grt_01KA3M7Q",
"seller": "pp:7f2c",
"operator": "https://agents.example/.well-known/ucp",
"agent_id": "agt:seller:7c1f",
"cnf": { "jkt": "0ZcOCORZNYy-DWpqq30jZyJGHTN0d2HglBV3uiguA4I" },
"scopes": ["dev.rctp.listing:create", "dev.rctp.listing:answer_buyer", "dev.rctp.deal:offer_counter", "dev.rctp.deal:offer_accept"],
"tiers": { "dev.rctp.deal:offer_accept": "ask" },
"iat": 1792540800, "exp": 1800316800, "status": "active"
}
A.2 Listing Terms (stored by the platform; never sent to an agent)
{
"listing_id": "lst_camera_x100",
"term_set": "goods.small/1",
"terms": {
"floor": { "value": 300, "currency": "SGD" },
"counter": { "value": 340, "currency": "SGD" },
"available_until": "2026-11-21T00:00:00Z",
"fulfillment_options": ["shipping", "meetup"],
"release_condition": "buyer_accepted",
"deliver_within_days": 7
},
"terms_commitment": "sha-256:5b1e..."
}
A.3 Offer with Deal Terms
{
"offer_id": "ofr_01KB2R9T",
"negotiation_id": "neg_01KB2Q4M",
"supersedes": "ofr_01KB2Q8C",
"expires_at": "2026-10-29T12:00:00Z",
"listing_id": "lst_camera_x100",
"price": { "value": 335, "currency": "SGD" },
"fulfillment": { "method_type": "shipping", "selected_option_id": "std" },
"payment": { "selected_term_id": "pay_now_held" }
}
A.4 Decisions
HTTP/1.1 403 Forbidden
RCTP-Decision: forbid;action="offer.create";reason=below_floor;counter=340.0
HTTP/1.1 202 Accepted
RCTP-Decision: ask;approval_id="apr_01K8ZB7";expires_in=900;on_timeout=deny
HTTP/1.1 429 Too Many Requests
RCTP-Decision: forbid;reason=offer_budget_exhausted;retry_after=3600
A.5 Deal record entries
[
{ "seq": 1, "author": "ep:marketplace.example", "action": "offer.create", "tier": "forbid", "result": "below_floor", "offered": 250 },
{ "seq": 2, "author": "ep:marketplace.example", "action": "offer.accept", "tier": "auto", "result": "accepted", "terms_commitment": "sha-256:5b1e..." },
{ "seq": 3, "author": "pay:holder.example", "event": "funds_held", "observation": "first_hand" },
{ "seq": 4, "author": "carrier.example", "event": "shipped", "observation": "first_hand", "evidence": { "tracking_ref": "sha-256:9a0c..." } },
{ "seq": 5, "author": "ep:marketplace.example", "event": "delivered", "observation": "relayed", "reported_by": "carrier.example", "source_event_id": "evt-55821", "location": "Singapore" },
{ "seq": 6, "author": "pp:buyer-3d1a", "event": "accepted" },
{ "seq": 7, "author": "pay:holder.example", "event": "released", "observation": "first_hand" }
]
A.6 Party Credential presentation (a buyer's disclosed claims)
{
"vct": "credential.counterparty.1",
"disclosed": {
"identity_assurance": { "level": "gov_eid", "source": "VNeID", "observation": "relayed" },
"buyer.completed_purchases_band": "10-49",
"buyer.adverse_findings_band": "0"
},
"kb_jwt": { "aud": "ep:marketplace.example", "nonce": "n-5f2c" }
}
Appendix B. Resolved open issues
Raised and resolved in #21; every resolution is decision D-31.
| ID | Question | Resolution | Section |
|---|---|---|---|
| OI-1 | May the seller agent pre-fill suggested terms? | No | 6.1 |
| OI-2 | Default tier for offer.accept within terms |
ask; the seller may choose auto |
8.3, 8.4 |
| OI-3 | First term sets | goods.small/1, vehicles/1; property later |
6.2, 16 |
| OI-4 | Grant lifetime and idle expiry | At most 90 days, renewed by refresh; earlier idle expiry at the marketplace's choice | 5.2 |
| OI-5 | May a seller let their own agent see the floor? | No | 6.4 |
| OI-6 | Draft 0.3 [9.2(4)]: fixed rule or recommended default? | Kept as a rule | 8.6 |
| OI-7 | RCTP scope names | Scopes under dev.rctp.listing and dev.rctp.deal (D-29, D-30) |
8.4 |
| OI-8 | UCP release to pin | v2026-08-25; UCP Ask at commit f2626c2 until released |
1.3, 18 |
| OI-9 | When to propose the offer part of dev.rctp.deal to UCP #502 |
After this review; to discussions #502 and #812, as a vendor-namespaced capability with vectors | — |
| OI-10 | Capability version string and overlap with 2026-10-draft3 |
2026-11-draft4; 2026-10-draft3 served beside it for at most 90 days |
4.5 |
| OI-11 | Decision header name | RCTP-Decision |
8.2 |
| OI-12 | Booking viewings and test drives before a deal | Out of scope; UCP Local Services once it exists | 11.5 |
| OI-13 | Offer capability name, given UCP's Offer extension (#738, #773) | dev.rctp.deal (D-29) |
7.1 |
Appendix C. Decisions and changes
C.1 Decision log
Every decision was adopted by the editors; D-20 to D-31 were confirmed in #21.
| ID | Date | Decision | Why | Status | Sections |
|---|---|---|---|---|---|
| D-1 | 2026-10-14 | Names dev.rctp.* under rctp.dev; capability version 2026-10-draft3; Starter profile first |
Neutral namespace from day one; smallest useful slice | Adopted | 4.5, 17.4 |
| D-2 | 2026-10-18 | Split the Seller Mandate into Agent Grant and Listing Terms | The mandate keyed floors by listing ID at connect time, so listings the agent created later could not be negotiated | Adopted | 5, 6 |
| D-3 | 2026-10-18 | The seller's permission is platform-local, not a verifiable credential | It is not enforced by any other party; revocation becomes live | Adopted | 5 |
| D-4 | 2026-10-18 | Accepting within terms is a per-seller tier | Some sellers want speed, others control | Adopted | 8.3 |
| D-5 | 2026-10-18 | A floor is enough; other terms vary per category through term sets | One protocol, category-appropriate safeguards | Adopted | 6.2 |
| D-6 | 2026-10-20 | The seller sets terms on the hosted page; no agent receives them | An agent cannot leak what it never had | Adopted | 6.1, 6.4 |
| D-7 | 2026-10-20 | Offer budget per buyer agent with an operator ceiling | One abusive user should not lock out a whole operator | Adopted | 8.5 |
| D-8 | 2026-10-20 | Refunds: not_as_described needs a completed return; not_received after the delivery window |
Close "keep the item and the money" and "never shipped" | Adopted | 10.3 |
| D-9 | 2026-10-21 | Minimum core on UCP and AP2: reuse what exists, keep five things | Simplicity is the ultimate complexity | Adopted | 1.2, 1.3 |
| D-10 | 2026-10-21 | RCTP covers proof and Deal Terms; booking, quotes and tracking stay with UCP and carriers | Do not redefine existing verbs | Adopted | 1.1, 7.2 |
| D-11 | 2026-10-21 | The holder of funds is a role: the AP2 payee | Works for managed marketplaces and licensed payment partners | Adopted | 7.5, 10.2 |
| D-12 | 2026-10-21 | Carrier- or inspector-signed checks are an optional category term | No opinion in the core | Adopted | 6.2, 10.3 |
| D-13 | 2026-10-21 | Buyer requirements are a Listing Terms requirement set by the seller | The seller's preference, enforced by the platform | Adopted | 6.2 |
| D-14 | 2026-10-21 | Agent identity is the UCP profile and UCP-Agent; the operator record is retired |
UCP already defines it | Adopted | 4.1 |
| D-15 | 2026-10-21 | The Agent Grant is UCP Identity Linking with RCTP scopes and key binding | UCP already defines OAuth scopes | Adopted | 5 |
| D-16 | 2026-10-21 | UCP signed webhooks replace the RCTP webhook format | UCP already defines them | Adopted | 1.3 |
| D-17 | 2026-10-21 | Events over UCP order events; RCTP keeps funds_held, accepted, rejected, released |
UCP covers shipping and money adjustments | Adopted | 10.2 |
| D-18 | 2026-10-21 | Offers carry Deal Terms; listings carry available_until |
Agents agree the whole deal, within a listing window | Adopted | 6.2, 7.2 |
| D-19 | 2026-10-21 | Six fixed rules; everything else is an open value | Minimal core, extensible by category | Adopted | 1.2 |
| D-20 | 2026-10-21 | terms_invalid also covers Deal Terms the listing does not allow |
One reason for one class of error | Adopted | 7.2, 13 |
| D-21 | 2026-10-21 | Handover options are part of Deal Terms; the separate handover capability is dropped | One place to agree how the item changes hands | Adopted | 11.5 |
| D-22 | 2026-10-21 | Starter profile: Core, Approval at hosted_embedded, Proof money events; item checks added on demand |
Matches what v0.1 proved | Adopted | 17.4 |
| D-23 | 2026-10-21 | Deal events written at POST /deals/{id}/events |
One route for all attested events | Adopted | 12 |
| D-24 | 2026-10-27 | Accepting an offer reserves the listing; other accepts get listing_reserved |
Second-hand items are single units; an auto accept could otherwise create two checkouts |
Adopted | 7.4, 8.1, 13 |
| D-25 | 2026-10-27 | Accepted Deal Terms are fixed until the checkout is redeemed or expires; only a seller withdrawal ends it early | The platform makes a firm commitment; later term changes must not reprice a deal | Adopted | 6.3, 7.4 |
| D-26 | 2026-10-27 | Every offer and counter has expires_at, capped at available_until |
Async negotiation must not leave offers open indefinitely | Adopted | 7.3 |
| D-27 | 2026-10-27 | Offers carry negotiation_id and supersedes |
Durable identity and revision across counters, as UCP discussion #812 asks | Adopted | 7.2 |
| D-28 | 2026-10-27 | The offer capability is standalone and produces a checkout, not a checkout extension | Offers exist before any checkout; matches the separate-artifact model in UCP #812 | Adopted | 7.1 |
| D-29 | 2026-10-27 | Two capabilities: dev.rctp.listing and dev.rctp.deal (offers through the deal record); every seller scope sits under one of them; fixed forbid verbs have no scope |
"Offer" clashes with UCP's merchant Offer extension (#738, #773); "deal" is already the draft's word for the trade, and both sides should get a good one; scopes must name a real capability | Adopted | 1.3, 5.2, 6.2, 7.1, 8.4 |
| D-30 | 2026-10-27 | order.commit_to_ship becomes deal.commit_to_ship; passport.disclose_full_report becomes deal.disclose_passport |
Every seller verb reads listing.*, offer.* or deal.*, so its prefix names its capability |
Adopted | 8.4 |
| D-31 | 2026-10-27 | Draft 0.4 adopted for v0.2; open issues OI-1 to OI-13 resolved as listed in Appendix B | Editor review complete (#21) | Adopted | 1.3, 4.5, 5.2, 6.1, 6.4, 8.2, 11.5, B |
| D-32 | 2026-10-27 | Erratum: RCTP scopes are declared in dev.ucp.common.identity_linking config.scopes, as every UCP scope is, not in the RCTP capability configs |
UCP Identity Linking defines one place for scopes; D-29 had moved them | Adopted | 5.2, 7.1 |
C.2 Resolved ambiguities from v0.1
| ID | Question | Resolution | Section |
|---|---|---|---|
| A-1 | How the enforcement point receives the mandate | Dissolved: no mandate travels; a key-bound token does | 5.3 |
| A-2 | SD-JWT+KB for the mandate | Dissolved: no credential is presented to the enforcement point | 5.3 |
| A-3 | Reason for an unverifiable mandate | grant_invalid, grant_revoked, grant_expired |
13 |
| A-4 | Counter equal to the floor | Term-set constraint counter > floor |
6.2 |
| A-5 | Offers after the budget is spent | All get 429; budget per agent with operator ceiling | 8.5 |
| A-6 | Action for an inbound offer | offer.create |
8.3 |
| A-7 | Listing scope; missing floor | Dissolved: the seller's listings with terms | 6.5 |
| A-8 | Floor on the seller's counter | Yes, and on reprices | 8.3 |
| A-9 | Who accepts | Seller side only; a counter is an invitation | 7.3 |
| A-10 | Late approval reason | approval_invalid; approval expired with approval_timeout |
9.3 |
| A-11 | Status-list caching versus next-call failure | Dissolved: grants are checked live | 5.4 |
| A-12 | What precedes refunded |
Rules 10.3(2)–(3) | 10.3 |
| A-13 | Seller passkey registration | The existing marketplace account passkey | 9.3 |
| A-14 | Routes for checkout, events, fixed forbids | Section 12; fixed forbids need no route | 12 |
C.3 Change history
| Version | Date | Status | Changes |
|---|---|---|---|
| Draft 0.2 | — | Superseded | Verified custody; buyer agent defaults; credential hardening; consumer-reporting check; two-implementation rule for Draft 1.0 |
| Draft 0.3 | 2026-10-10 | Superseded; implemented by v0.1.0-draft.3 |
Seller and buyer sections in the Counterparty Credential; identity facts module; contact and delivery disclosure by stage; owner and return-match attestations; first-hand and relayed checkpoints mapped to EPCIS; partner operator obligations; AP2 buyer authority; approval interface; Starter profile |
| Draft 0.4 | 2026-10-21 | Editor's draft | Agent Grant and Listing Terms replace the Seller Mandate (D-2 to D-8); minimum core on UCP and AP2 (D-9 to D-19); offers with Deal Terms; deal record over UCP order events; operator record and RCTP webhooks retired; reason codes grant_* and terms_invalid; invariants INV-1, INV-5, INV-7 restated; vectors V10–V15 added; all fourteen v0.1 ambiguities resolved (C.2) |
| Draft 0.4 | 2026-10-27 | Editor's draft | Review fixes D-24 to D-30: listing reservation and listing_reserved; fixed Deal Terms after acceptance; expires_at on every offer; negotiation_id and supersedes; capabilities dev.rctp.listing and dev.rctp.deal with scopes under them (OI-7, OI-13 resolved); verbs deal.commit_to_ship and deal.disclose_passport; INV-6 restated; vectors V26–V29 |
| Draft 0.4 | 2026-10-27 | Adopted for v0.2 | Open issues OI-1 to OI-13 resolved (D-31); UCP v2026-08-25 pinned; capability version 2026-11-draft4 |
| Draft 0.4 | 2026-10-27 | Adopted for v0.2, erratum | D-32: scopes declared in UCP Identity Linking config.scopes |
Source: spec/rctp-draft-0.4.md