RPP402-005Draft

RPP402-005: Settlement

RFCRPP402-005
TitleSettlement
StatusDraft
Version1.0.0
RequiresRPP402-004

Overview

Settlement is the execution of an authorized Payment Intent as an onchain transaction on Robinhood Chain. It is where RPP402 stops being an off-chain coordination protocol and becomes a set of asset transfers with finality. A Settlement always references exactly one Payment Intent and produces exactly one Receipt (RPP402-006).

Motivation

Everything before Settlement (Discovery, Quote, Session, Intent) is coordination -agreement between agent and Service about what will happen. Settlement is where it actually happens, on a chain neither party can unilaterally rewrite. Splitting "agreed" (Intent) from "happened" (Settlement) into two primitives, rather than collapsing them the way a single 402 cycle does, is what lets a Session hold multiple legs safely: every leg's payment is authorized together (one Intent per asset) but the chain interaction that finalizes it is its own auditable step.

Problem

Settlement has to handle two asset paths differently (stablecoin vs. tokenized security, per RPP402-004), has to be idempotent (a retried Settlement request must not double-spend), and has to expose enough onchain detail that a Receipt (RPP402-006) can be independently verified without trusting the Service's word for it.

Terminology

TermDefinition
SettlementThe settle_ object defined below.
Asset transferOne onchain transfer within a Settlement -a Settlement covering a multi-leg Session with several recipients may contain several transfers in one transaction.
FinalityThe point after which a confirmed Settlement cannot be reorganized away -see Security.

Protocol

Settlement is initiated automatically by the Service once a Payment Intent reaches authorized; there is no separate "start settlement" call an agent makes manually beyond authorizing the Intent. A Settlement object is created and its status tracked, readable at GET {endpoints.session}/{id}/settlement (see RPP402-003 §Endpoints) once it exists:

FieldTypeDescription
rpp402_versionstring"1.0".
idstringsettle_ prefixed.
intent_idstringThe Payment Intent this executes.
session_idstringDenormalized from the Intent for convenience.
chain_idstring"robinhood-1".
tx_hashstring | nullSet once submitted onchain.
block_numberinteger | nullSet once included in a block.
statusstringpendingsubmittedconfirmed | failed.
transfersarray<Transfer>One per leg's recipient; see below.
reference_priceReferencePrice | nullPresent only when the Intent's asset is tokenized_security with amount_mode: "notional_usd" -see RPP402-004.
created_atstring (RFC 3339)
confirmed_atstring (RFC 3339) | null

Transfer:

json
{ "from": "0xaaaa...1234", "to": "0x1111...aaaa", "asset": { "...": "AssetRef" }, "amount": "0.002" }

ReferencePrice (notional-mode tokenized-security settlements only):

json
{ "oracle": "robinhood-chain-price-oracle", "symbol": "NVDA.rh", "price_usd": "132.50", "sourced_at": "2026-07-10T18:24:03Z" }

Asset-specific settlement paths

  • Stablecoin: a direct token transfer per leg recipient, batched into one transaction when the Intent covers multiple legs in the same asset.
  • Tokenized security: transfer of the security token contract, with fixed_quantity executing directly and notional_usd first resolving amount / reference_price.price_usd into a token quantity, then transferring that quantity. For agents settling via agentic_account_delegation (RPP402-004), the transfer additionally releases the corresponding collateral hold on the Agentic Account rather than moving from a plain wallet balance.

Settlement submission is idempotent on intent_id: resubmitting a Settlement request for an Intent that already has a non-failed Settlement returns the existing object rather than creating a second one.

JSON Schema

json
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://rpp402.com/schemas/v1/settlement.json",
  "title": "RPP402 Settlement",
  "type": "object",
  "required": ["rpp402_version", "id", "intent_id", "session_id", "chain_id", "status", "transfers", "created_at"],
  "properties": {
    "rpp402_version": { "type": "string", "const": "1.0" },
    "id": { "type": "string", "pattern": "^settle_[a-zA-Z0-9]{8,}$" },
    "intent_id": { "type": "string", "pattern": "^intent_[a-zA-Z0-9]{8,}$" },
    "session_id": { "type": "string", "pattern": "^sess_[a-zA-Z0-9]{8,}$" },
    "chain_id": { "type": "string" },
    "tx_hash": { "type": ["string", "null"], "pattern": "^0x[a-fA-F0-9]{64}$" },
    "block_number": { "type": ["integer", "null"] },
    "status": { "type": "string", "enum": ["pending", "submitted", "confirmed", "failed"] },
    "transfers": {
      "type": "array",
      "minItems": 1,
      "items": {
        "type": "object",
        "required": ["from", "to", "asset", "amount"],
        "properties": {
          "from": { "type": "string", "pattern": "^0x[a-fA-F0-9]{40}$" },
          "to": { "type": "string", "pattern": "^0x[a-fA-F0-9]{40}$" },
          "asset": { "$ref": "https://rpp402.com/schemas/v1/asset-ref.json" },
          "amount": { "type": "string", "pattern": "^[0-9]+(\\.[0-9]+)?$" }
        }
      }
    },
    "reference_price": {
      "type": ["object", "null"],
      "properties": {
        "oracle": { "type": "string" },
        "symbol": { "type": "string" },
        "price_usd": { "type": "string" },
        "sourced_at": { "type": "string", "format": "date-time" }
      }
    },
    "created_at": { "type": "string", "format": "date-time" },
    "confirmed_at": { "type": ["string", "null"], "format": "date-time" }
  }
}

Examples

json
{
  "rpp402_version": "1.0",
  "id": "settle_c91a4f",
  "intent_id": "intent_9a1b02",
  "session_id": "sess_9a1b02",
  "chain_id": "robinhood-1",
  "tx_hash": "0x7e2f...c9a1",
  "block_number": 4821093,
  "status": "confirmed",
  "transfers": [
    {
      "from": "0xaaaa...1234",
      "to": "0x2222...bbbb",
      "asset": { "type": "tokenized_security", "symbol": "NVDA.rh", "chain_id": "robinhood-1", "contract": "0x2222...bbbb", "issuer": "Robinhood Assets (Jersey) Limited" },
      "amount": "0.3773"
    }
  ],
  "reference_price": { "oracle": "robinhood-chain-price-oracle", "symbol": "NVDA.rh", "price_usd": "132.50", "sourced_at": "2026-07-10T18:24:03Z" },
  "created_at": "2026-07-10T18:24:01Z",
  "confirmed_at": "2026-07-10T18:24:03Z"
}

(0.3773 ≈ 50.00 / 132.50, matching the notional-mode Intent example in RPP402-004.)

Security

  • A Settlement MUST NOT submit onchain if the referenced Intent's status is anything other than authorized, or if the Intent's expires_at has passed.
  • Finality follows Robinhood Chain's own finality guarantees (Arbitrum-Orbit L2, ~100ms blocks with L1 finality lagging behind); a Settlement's status only moves to confirmed after the block is final by the chain's own rules, not merely included.
  • reference_price MUST be sourced from the same oracle a corresponding Receipt (RPP402-006) discloses -an agent or auditor must be able to independently re-check the conversion after the fact, not just trust the Service's arithmetic.
  • Idempotency (above) is itself a security property: without it, a network retry of a Settlement submission could otherwise be misread as agent intent to pay twice.

Errors

typestatusMeaning
https://rpp402.com/errors/intent-not-authorized409Intent isn't authorized.
https://rpp402.com/errors/intent-expired410
https://rpp402.com/errors/settlement-failed502Onchain transaction reverted or failed to confirm; see status: "failed" object for detail. A failed Settlement requires a new Session -Sessions are not reopened (see RPP402-003).
https://rpp402.com/errors/insufficient-balance402Payer's balance (or Agentic Account collateral) is insufficient at execution time.

Best Practices

  • Treat status: "submitted" as provisional; only "confirmed" is safe to treat as final for accounting purposes.
  • For notional-mode tokenized-security settlements, always persist reference_price alongside the Settlement in any downstream system -it's the only record of what conversion actually happened.
  • Don't retry a failed Settlement's Intent; start a new Session. The Intent that failed is bound to the Session's leg set at the moment it failed and cannot be safely reused.

Reference Implementation

Schema: packages/protocol -settlement.schema.json (Phase 4, this phase). Onchain execution: packages/contracts (tracked alongside Phase 4/5). Client: session.settle() in @rpp402/sdk (Phase 5).


Previous: RPP402-004 -Payment Intent · Next: RPP402-006 -Receipt