RPP402-002: Quote
| RFC | RPP402-002 |
| Title | Quote |
| Status | Draft |
| Version | 1.0.0 |
| Requires | RPP402-001 |
Overview
A Quote is a Service's binding, time-limited price for one Capability call, in one Settlement Asset. An agent requests a Quote directly from the endpoints.quote URL a Discovery Document (RPP402-001) points to. A Quote is a standalone object -it exists before any Commerce Session and can be requested from several Services to compare, with no obligation to purchase.
Motivation
Pricing is the first decision point in any purchase. MPP and x402 fold pricing into the 402 challenge itself -a client sees a price only in the act of being asked to pay for that exact resource, with no separate step to shop around. RPP402 separates Quote from purchase specifically so an agent can request Quotes from three Services, compare them, and only then start a Commerce Session with the one it picked. This is the first of the two structural differences from prior art named in RPP402-000.
Problem
An agent needs a price it can act on later -not an estimate that might have moved by the time it decides. A Quote must therefore be both binding (the Service commits to it) and expiring (the Service isn't on the hook forever, since underlying costs, and for tokenized-asset pricing, underlying market prices, move).
Terminology
| Term | Definition |
|---|---|
| Quote | A binding, time-limited price for one Capability call. |
| Quote Request | The input an agent sends to request one. |
expires_at | The instant after which a Quote can no longer be accepted into a Payment Intent (RPP402-004). |
Protocol
POST {endpoints.quote} with a QuoteRequest:
| Field | Type | Required | Description |
|---|---|---|---|
capability | string | yes | Must match a capabilities[].name from the service's Discovery Document. |
params | object | no | Capability-specific parameters (e.g. { "ticker": "NVDA" }). Opaque to RPP402 itself. |
settlement_asset | AssetRef | yes | Which supported asset (from Discovery) the agent wants priced in. |
agent_wallet | string | yes | The requesting agent's wallet address -lets a Service return agent-specific pricing tiers if it offers them, and is echoed back into the Quote for later verification. |
Response, a Quote object:
| Field | Type | Description |
|---|---|---|
rpp402_version | string | "1.0". |
id | string | quote_ prefixed, globally unique. |
service_id | string | Echoes the Service's service.id. |
capability | string | Echoes the request. |
price | Money | { asset: AssetRef, amount: string } -see below. |
expires_at | string (RFC 3339) | When this Quote stops being acceptable. |
created_at | string (RFC 3339) |
Money -shared across RPP402-002 through RPP402-006:
{ "asset": { "type": "stablecoin", "symbol": "USDG", "chain_id": "robinhood-1", "contract": "0x..." }, "amount": "0.002" }amount is always a decimal string, never a float -floats cannot represent every valid onchain amount exactly, and a payments protocol that lets its own numbers round is a defect (Design Principle 3, DESIGN-SYSTEM.md §3).
A Quote is not itself a reservation -a Service is not obligated to guarantee capacity, only price, until an agent starts a Commerce Session (RPP402-003) that references it.
JSON Schema
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://rpp402.com/schemas/v1/quote.json",
"title": "RPP402 Quote",
"type": "object",
"required": ["rpp402_version", "id", "service_id", "capability", "price", "expires_at", "created_at"],
"properties": {
"rpp402_version": { "type": "string", "const": "1.0" },
"id": { "type": "string", "pattern": "^quote_[a-zA-Z0-9]{8,}$" },
"service_id": { "type": "string", "pattern": "^svc_[a-zA-Z0-9]{8,}$" },
"capability": { "type": "string", "pattern": "^[a-z0-9-]+\\.[a-z0-9-]+$" },
"price": {
"type": "object",
"required": ["asset", "amount"],
"properties": {
"asset": { "$ref": "https://rpp402.com/schemas/v1/asset-ref.json" },
"amount": { "type": "string", "pattern": "^[0-9]+(\\.[0-9]+)?$" }
}
},
"expires_at": { "type": "string", "format": "date-time" },
"created_at": { "type": "string", "format": "date-time" }
}
}Examples
Request:
POST https://market-data.example/rpp/quote
{
"capability": "market-data.quote",
"params": { "ticker": "NVDA" },
"settlement_asset": { "type": "stablecoin", "symbol": "USDG", "chain_id": "robinhood-1", "contract": "0x1111...aaaa" },
"agent_wallet": "0xaaaa...1234"
}Response:
{
"rpp402_version": "1.0",
"id": "quote_8f2e9a1c",
"service_id": "svc_marketdata01",
"capability": "market-data.quote",
"price": {
"asset": { "type": "stablecoin", "symbol": "USDG", "chain_id": "robinhood-1", "contract": "0x1111...aaaa" },
"amount": "0.002"
},
"expires_at": "2026-07-10T18:32:00Z",
"created_at": "2026-07-10T18:22:00Z"
}Security
- A Quote is not authenticated payment authority -possessing a
quote_iddoes not let anyone spend anything. It only becomes economically meaningful once referenced by a Payment Intent (RPP402-004), which requires the agent's own signature. - Services SHOULD rate-limit Quote requests per
agent_walletto prevent quote-spam from being used as a free pricing-oracle scrape at the expense of real capacity planning. expires_atMUST be enforced server-side at Payment Intent authorization time (RPP402-004), not just advisory on the client.
Errors
type | status | Meaning |
|---|---|---|
https://rpp402.com/errors/capability-not-found | 404 | capability doesn't match any capability the service offers. |
https://rpp402.com/errors/asset-not-supported | 422 | settlement_asset isn't in the service's supported_assets. |
https://rpp402.com/errors/quote-expired | 410 | Referenced later (RPP402-003/004) after expires_at has passed. |
Best Practices
- Request Quotes from every candidate Service concurrently when comparison-shopping; don't serialize.
- Treat a Quote's
expires_atas a hard deadline for starting the Commerce Session leg that references it -build in margin, since network latency plus agent decision time both eat into the window. - When priced in a
tokenized_securityasset, re-read RPP402-004 §Settlement Assets before assumingamountmeans "token quantity" -it may mean "USD notional," and the two require different downstream handling.
Reference Implementation
Schema: -packages/protocolquote.schema.json (Phase 4, this phase). Client: rpp.quote(request) in @rpp402/sdk (Phase 5).
Previous: RPP402-001 -Discovery · Next: RPP402-003 -Commerce Session
