RPP402-004: Payment Intent
| RFC | RPP402-004 |
| Title | Payment Intent |
| Status | Draft |
| Version | 1.0.0 |
| Requires | RPP402-003 |
Overview
A Payment Intent is an agent's signed authorization to pay a Commerce Session's total, in a specific asset, to specific recipients. It is the last step before Settlement (RPP402-005) and the point at which the Session's leg set freezes. RPP402-004 is also where the strategy decision locked in becomes wire format: v1 supports both stablecoin and tokenized-real-world-asset settlement, not stablecoins only.PRODUCT-STRATEGY.md
Motivation
Every prior-art payment protocol assumes the payer's spending authority is a stablecoin balance. That assumption doesn't hold for an agent operating inside a Robinhood Agentic Account, whose actual capital may be tokenized equity exposure. Restricting RPP402 to stablecoins-only would mean an Agentic Account agent has to off-ramp to a stablecoin before it can transact -an extra step, an extra fee, and a missed opportunity to demonstrate the one thing no other protocol can do. RPP402-004 makes the asset a first-class dimension of the Intent instead of assuming it away.
Problem
A Payment Intent has to answer three questions unambiguously before Settlement executes: how much, in what, and who authorized it. "How much" is easy for a stablecoin (a fixed decimal amount). It is not easy for a tokenized security, whose market price moves between Quote time and Settlement time -so RPP402-004 has to specify which of "a fixed number of tokens" or "a fixed USD notional, converted at settlement-time price" the Intent means, explicitly, per Intent.
Terminology
| Term | Definition |
|---|---|
| Payment Intent | The intent_ object defined below. |
| Settlement Asset | The asset (stablecoin or tokenized security) an Intent is denominated and paid in. |
| Fixed-quantity mode | A tokenized-security Intent that authorizes transfer of an exact token quantity. |
| Notional mode | A tokenized-security Intent that authorizes transfer of whatever token quantity equals a fixed USD notional at settlement-time reference price. |
| Delegated authority | Authorization sourced from a Robinhood Agentic Account's pre-granted capital/risk limits rather than a fresh signature. |
Protocol
POST {endpoints.session}/{id}/intent (see RPP402-003 §Endpoints), issued against a Session in quoting status with ≥1 leg:
| Field | Type | Required | Description |
|---|---|---|---|
session_id | string | yes | Must be quoting with ≥1 leg. |
payer_wallet | string | yes | Must match the Session's agent_wallet. |
asset | AssetRef | yes | Must match exactly one asset already present across the Session's legs (a Session with legs in two assets requires two Intents, one per asset -see RPP402-003). |
amount_mode | string | required if asset.type == "tokenized_security", omitted otherwise | "fixed_quantity" or "notional_usd". Stablecoin Intents have no mode -the amount is always the exact decimal sum of the covered legs. |
authorization | Authorization | yes | See below. |
Response, a PaymentIntent object:
| Field | Type | Description |
|---|---|---|
rpp402_version | string | "1.0". |
id | string | intent_ prefixed. |
session_id | string | |
payer_wallet | string | |
asset | AssetRef | |
amount_mode | string | null | Echoes the request. |
amount | string | The signed amount: token quantity (fixed_quantity or stablecoin) or USD notional (notional_usd). Always a decimal string. |
status | string | pending → authorized → (settling is Settlement's concern, not the Intent's) | revoked | expired. |
authorization | Authorization | Echoed back with server-assigned nonce. |
created_at | string (RFC 3339) | |
expires_at | string (RFC 3339) | Inherits the Session's expires_at unless the authorization mechanism imposes a shorter one. |
Authorization
Two mechanisms in v1, distinguished by authorization.type:
eip712_signature -the general case. The agent signs an EIP-712 typed-data payload over { session_id, payer_wallet, asset, amount_mode, amount, nonce, expires_at } with the wallet's private key.
{ "type": "eip712_signature", "signer": "0xaaaa...1234", "signature": "0x...", "nonce": "0x9f3a..." }agentic_account_delegation -for agents operating inside a Robinhood Agentic Account, where spending authority was already granted by the account owner as a capital/risk-limit policy rather than requested per-transaction. The Intent references that delegation instead of producing a fresh signature.
{ "type": "agentic_account_delegation", "account_id": "aa_7d21f0", "delegation_ref": "del_44a1", "risk_check": "within_limit" }A Service or the Settlement layer (RPP402-005) MUST independently verify a delegation reference against Robinhood Chain's Agentic Account registry rather than trusting the client-asserted risk_check value -that field is informational for the agent's own logging, not a security control.
Settlement Assets
This section is the substantive difference from MPP and x402 named in RPP402-000:
stablecoin-behaves like any other payment protocol:amountis an exact decimal quantity of the stablecoin, noamount_mode.tokenized_security-amount_modeis required:"fixed_quantity":amountis an exact token quantity (e.g."0.014"shares ofNVDA.rh). Settlement transfers exactly that quantity regardless of price movement between Intent and Settlement."notional_usd":amountis a USD figure (e.g."50.00"). Settlement (RPP402-005) converts it to a token quantity at the reference price sourced at settlement time and records both the USD notional and the resulting quantity on the Receipt (RPP402-006), so "how many tokens did this actually cost" is always reconstructable after the fact.
A Service's Discovery Document (RPP402-001) supported_assets entry for a tokenized_security does not by itself imply which amount_modes it accepts -that is negotiated at Intent time; a Service MAY reject an Intent whose amount_mode it doesn't support with unsupported-amount-mode (see Errors).
JSON Schema
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://rpp402.com/schemas/v1/payment-intent.json",
"title": "RPP402 Payment Intent",
"type": "object",
"required": ["rpp402_version", "id", "session_id", "payer_wallet", "asset", "amount", "status", "authorization", "created_at", "expires_at"],
"properties": {
"rpp402_version": { "type": "string", "const": "1.0" },
"id": { "type": "string", "pattern": "^intent_[a-zA-Z0-9]{8,}$" },
"session_id": { "type": "string", "pattern": "^sess_[a-zA-Z0-9]{8,}$" },
"payer_wallet": { "type": "string", "pattern": "^0x[a-fA-F0-9]{40}$" },
"asset": { "$ref": "https://rpp402.com/schemas/v1/asset-ref.json" },
"amount_mode": { "type": ["string", "null"], "enum": ["fixed_quantity", "notional_usd", null] },
"amount": { "type": "string", "pattern": "^[0-9]+(\\.[0-9]+)?$" },
"status": { "type": "string", "enum": ["pending", "authorized", "revoked", "expired"] },
"authorization": {
"oneOf": [
{
"type": "object",
"required": ["type", "signer", "signature", "nonce"],
"properties": {
"type": { "const": "eip712_signature" },
"signer": { "type": "string", "pattern": "^0x[a-fA-F0-9]{40}$" },
"signature": { "type": "string", "pattern": "^0x[a-fA-F0-9]+$" },
"nonce": { "type": "string" }
}
},
{
"type": "object",
"required": ["type", "account_id", "delegation_ref"],
"properties": {
"type": { "const": "agentic_account_delegation" },
"account_id": { "type": "string", "pattern": "^aa_[a-zA-Z0-9]{4,}$" },
"delegation_ref": { "type": "string", "pattern": "^del_[a-zA-Z0-9]{4,}$" },
"risk_check": { "type": "string" }
}
}
]
},
"created_at": { "type": "string", "format": "date-time" },
"expires_at": { "type": "string", "format": "date-time" }
},
"if": { "properties": { "asset": { "properties": { "type": { "const": "tokenized_security" } } } } },
"then": { "required": ["amount_mode"] }
}Examples
Stablecoin Intent (no amount_mode):
{
"session_id": "sess_3fd8c2",
"payer_wallet": "0xaaaa...1234",
"asset": { "type": "stablecoin", "symbol": "USDG", "chain_id": "robinhood-1", "contract": "0x1111...aaaa" },
"authorization": { "type": "eip712_signature", "signer": "0xaaaa...1234", "signature": "0x9d4e...", "nonce": "0x9f3a..." }
}Tokenized-security Intent, notional mode, via Agentic Account delegation:
{
"session_id": "sess_9a1b02",
"payer_wallet": "0xaaaa...1234",
"asset": { "type": "tokenized_security", "symbol": "NVDA.rh", "chain_id": "robinhood-1", "contract": "0x2222...bbbb", "issuer": "Robinhood Assets (Jersey) Limited" },
"amount_mode": "notional_usd",
"amount": "50.00",
"authorization": { "type": "agentic_account_delegation", "account_id": "aa_7d21f0", "delegation_ref": "del_44a1", "risk_check": "within_limit" }
}Security
- An
eip712_signatureIntent MUST include a server-issuednonce, single-use, to prevent replay -a captured signed Intent cannot be resubmitted for a second Settlement. expires_atbounds how long a signature or delegation reference remains valid for Settlement; RPP402-005 MUST reject a Settlement attempt against an expired Intent even if the signature itself is still cryptographically valid.agentic_account_delegationauthorizations MUST be checked against Robinhood Chain's Agentic Account registry at Settlement time, not cached from Intent-authorization time -the account owner may have revoked or reduced the delegation in between.- Notional-mode tokenized-security Intents are a price-oracle dependency: Settlement (RPP402-005) MUST source the reference price from the same oracle feed the Receipt (RPP402-006) discloses, so the conversion is independently auditable after the fact, not just asserted.
Errors
type | status | Meaning |
|---|---|---|
https://rpp402.com/errors/session-not-quoting | 409 | Session isn't in quoting status. |
https://rpp402.com/errors/asset-mode-required | 422 | tokenized_security asset without amount_mode. |
https://rpp402.com/errors/unsupported-amount-mode | 422 | Service doesn't accept the requested amount_mode for this asset. |
https://rpp402.com/errors/signature-invalid | 401 | EIP-712 signature doesn't verify. |
https://rpp402.com/errors/delegation-revoked | 403 | agentic_account_delegation reference no longer valid. |
https://rpp402.com/errors/nonce-reused | 409 | Replay attempt. |
Best Practices
- Prefer
notional_usdfor tokenized-security purchases where the agent cares about cost predictability more than exact share count; preferfixed_quantitywhen the agent specifically wants a target position size. - Agents operating under an Agentic Account should default to
agentic_account_delegationrather than requesting a fresh signature per Intent -it's both faster and keeps spending inside the account owner's pre-set risk limits rather than requiring a new ad hoc approval. - Always check
PaymentIntent.expires_atbefore assuming anauthorizedIntent is still settleable -an Intent does not become invalid by itself, but Settlement will reject it past expiry.
Reference Implementation
Schema: -packages/protocolpayment-intent.schema.json (Phase 4, this phase). Client: session.intent() in @rpp402/sdk (Phase 5). Onchain verification of both authorization types: (tracked alongside Phase 4/5, see that package's README).packages/contracts
Previous: RPP402-003 -Commerce Session · Next: RPP402-005 -Settlement
