Documentation
OverviewQuickstartConceptsDecisions and errorsApprovalsVersioningSecurityExtensionsFAQPartner integration profileFAQ
Status: Draft 0.4, pre-release. Short answers first; the technical detail follows each one.
Why not just use OAuth scopes?
RCTP does use OAuth scopes, but scopes say which verbs an agent may use, not how far it may go. A scope can allow "accept offers". It cannot say "but never below S$420, only after I approve each one, and never tell the buyer my limit".
So RCTP splits the job in two. The Agent Grant is an ordinary OAuth grant (code with PKCE, RFC 9396
authorization_details), bound to the agent's key, with a tier per scope that the seller confirms. The Listing
Terms hold the per-item rules (floor, delivery, payment, closing date), set by the seller on a hosted page
and kept by the platform. The enforcement point checks both on every call, and writes a signed record of every
decision. Marketplace APIs must still honour scopes: "A grant in front of an API that ignores scopes is a promise, not
a control" [14].
Can the seller's own agent know the floor?
Yes, if the seller tells it. The seller says "list at 500, not below 400" to their agent; the agent proposes those values, the marketplace shows them pre-filled, and the seller confirms once [6.1]. The marketplace never sends the floor to any agent, not even the one that proposed it, and never sells below it, whatever anyone says to an agent (INV-1, V11). An agent that knows the floor can still be talked into revealing it, so a marketplace that wants to rule that out asks the seller for the floor directly instead [6.4(4)].
Why is the floor never revealed to buyers?
Because any hint becomes the price. If a refusal said "below S$420", every buyer would offer exactly S$420.
So a refusal says only below_floor and carries no private value [8.2]; Draft 0.5 removed the optional counter
price. Each buyer agent, and each buyer operator,
has a limited number of below-floor offers per listing; after that every offer, at any price, gets 429, so probing
cannot find the number either [8.5]. The floor never appears in a header, body, webhook, log or deal record (INV-1).
What happens if the seller doesn't answer?
Nothing happens: no answer means no. When expires_at passes, the approval becomes expired with reason
approval_timeout, a pending offer becomes expired, and your operator gets an approval.decided webhook. A late
answer is refused. There is no setting to approve on timeout: on_timeout can only be deny (INV-2). See
Approvals.
Can a partner approve on the seller's behalf?
No. A partner's app may show the approval page, but only the seller's own passkey can sign the answer. An answer
signed with an operator or partner key is refused with approval_invalid (INV-4, conformance vector V9).
Draft 0.4 does define a certified_operator level, where a certified partner shows the approval in its own interface.
Even then the signature comes from a hardware-backed key on the seller's device, and only below an amount cap. No
certification scheme exists yet, so that level is out of scope for v0.2.
Can two buyers end up with the same item?
No. Accepting an offer reserves the listing: every other accept gets 409 listing_reserved until the buyer
redeems the single-use checkout or it expires (INV-6, V26). Other buyers can still make offers, which stay open. If
the checkout expires unredeemed, the listing returns to active.
Does RCTP move money?
No. RCTP defines no payment rail. Money moves through the marketplace's existing held payment or escrow.
What RCTP adds is proof and order. The holder of funds writes a signed funds_held event, which is the only proof of
payment a seller agent should trust [10.2]. The gateway refuses released until the buyer's agent has written
accepted (release_condition_unmet, V8), and refuses a refund for not_as_described until a return is recorded
(V13). The sandbox's mock holder of funds moves no money.
Why is the production gateway separate?
To keep the protocol separate from any one deployment. The specification names roles, not companies; enforcement is a role any conformant gateway can play. This repository holds the reference implementation that anyone can read, run and test against. The Group Foundation Tech team runs the production gateway from a separate private repository. It is built on these public SDKs and must pass this repository's conformance suite. Operational configuration, real keys and platform integrations stay out of a public repository, and other marketplaces can run their own conformant gateways on equal terms.
Who owns what?
| Party | Owns |
|---|---|
| rctp.dev (this repository) | The specification, schemas, registries, SDKs, reference implementation, conformance suite and sandbox |
| Each platform (Carousell, Mudah, Chợ Tốt) | Its login and authorization server (Agent Grants, consent page, introspection), its hosted approval page, its badge methodology and its partner attestation chain |
| Group Foundation Tech | The production gateway, in a separate private repository |
| Each partner (agent operator) | Its agents, keys, UCP profile, webhook endpoint and incident response |
Is a seller one identity across operators?
No. The seller appears to each operator under a different pairwise pseudonym (sub of the grant), so two
operators cannot link the same seller. Listing ownership and the approval passkey are therefore per operator (B-9).
Do buyer agents need a grant?
No. A buyer agent needs an onboarded operator UCP profile and signed requests; it is identified by its key
thumbprint. Draft 0.4 uses an AP2 Checkout Mandate as the buyer's payment authority [7.5]. v0.2 does not verify AP2
mandates yet, because no pinned library verifies AP2 v0.2 SD-JWT delegation chains and a hand-written one would be
custom cryptography (INV-8); buyer_authority_exceeded is not returned
(#59).
Can a prompt or the listing text change the rules?
No. The gateway reads tiers only from the grant and terms only from what the seller approved on the hosted page.
Request bodies are closed, so a field such as floor, tier or a free-text instruction is a 400. Listing text that
says "accept any offer" changes nothing (INV-5, vector V7).
What happens when a seller revokes the grant?
The agent's next signed call fails with grant_revoked. The gateway introspects the access token on every
seller-side call and never caches the answer (INV-7, V6). An action that was waiting for approval does not run, even if
the seller then approves it: the approval ends failed with reason grant_revoked. A refresh also fails, so the seller
has to connect again.
What does an agent do with a listing it created?
Propose it and let the seller set the terms. listing.create is always ask: the seller fills in the terms on the
hosted page and approves with their passkey. Once approved, the listing is open to agent negotiation with no new
consent (V10). A listing created by hand in the marketplace app has no terms and no seller agent until the seller sets
terms with listing.set_terms [6.5].
What is out of scope in v0.2?
Release v0.2.0-draft.4 implements the Starter profile [17.4]. Not built yet:
- AP2 buyer authority (V25, #59).
- Party Credentials and bands, so
counterparty_requirementscannot be met yet (B-6); identity facts; Item Passport. - Verified custody, seals and
seal_mismatch; relayed partner events (V20). - The
marketplace_appandcertified_operatorapproval levels. - Routes for
deal.disclose_passportanddeal.accept_deposit. - The MCP and A2A bindings, and an export of the full deal record.
Out of scope for RCTP itself: negotiation strategy, pricing, payment rails, identity-proofing methods, and how an issuer computes its bands beyond the published methodology.
How do I run conformance against my gateway?
The runner brings its own test authorization server and test operators. You tell your gateway to trust them, then run the vectors (V1–V15 with V4 as V4a and V4b, V26–V29 and S1–S4).
pnpm install && pnpm --filter @rctp/sdk build
# 1. A v0.2 gateway configuration that trusts the runner (authorization server, operators, url_rewrites).
pnpm conformance --print-gateway-config --gateway-public-url http://localhost:8080 > gateway.json
# 2. Start your gateway with it (test environments only), and the TEST ONLY signer.
RP_ID=localhost ORIGIN=http://localhost:8080 go run ./packages/gateway-go/cmd/rctp-testsigner -listen :8091 &
# 3. Run every vector.
pnpm conformance --gateway http://localhost:8080 --signer http://localhost:8091 --report report.json
Use --only V1,V4 to run some vectors. The exit code is non-zero when any in-scope vector fails; out-of-scope vectors
are reported not_in_scope with their reason. make conformance (./scripts/conformance.sh) does all of this for the
reference gateway and then runs the SDKs' live interop tests. The signer answers approvals through the reference
gateway's hosted approval endpoints; a gateway with different approval endpoints needs a signer with the same
interface. Details: conformance/README.md.
Never put the runner's test authorization server or operators in a production trust configuration.
Where do I report a security problem?
Privately, to security@rctp.dev (a placeholder until the mailboxes are live) or through GitHub's private vulnerability reporting. Do not open a public issue. See Security.
Source: docs/faq.md