{"templateId":"markdown","sharedDataIds":{},"props":{"metadata":{"markdoc":{"tagList":[]},"type":"markdown"},"seo":{"title":"Payments, Escrow & Agentic Patterns","siteUrl":"https://xrpl.org/","llmstxt":{"hide":false,"title":"XRPL Developer Portal & Documentation","description":"Explore XRP Ledger documentation, blogs, and other blockchain developer resources needed to start building and integrating with the ledger.","details":{"content":"XRP Ledger concepts, use cases, tutorials, references, and other blockchain developer resources. Also, stay up to date with release announcements and more through the XRPL Blog."},"sections":[{"title":"Introduction","description":"A high-level introduction to the XRP Ledger.","includeFiles":["docs/introduction/**/*.*","about/faq.md"],"excludeFiles":["docs/introduction/index.md"]},{"title":"Use Cases","description":"Real-world applications and business scenarios for the XRP Ledger.","includeFiles":["docs/use-cases/**/*.*"],"excludeFiles":["docs/use-cases/index.md","docs/use-cases/defi/index.md"]},{"title":"Agentic Transactions","description":"XRPL AI Starter Kit to help autonomous agents discover, set up, and execute agentic transactions on the XRP Ledger.","includeFiles":["docs/agents/**/*.*"],"excludeFiles":[]},{"title":"Concepts","description":"Core concepts including accounts, tokens, transactions, consensus, and more.","includeFiles":["docs/concepts/**/*.*"],"excludeFiles":["docs/concepts/index.md","docs/concepts/decentralized-storage/index.md","docs/concepts/payment-types/index.md"]},{"title":"Tutorials","description":"Step-by-step guides for building on the XRP Ledger in JavaScript, Python, Go, and more.","includeFiles":["docs/tutorials/**/*.*"],"excludeFiles":[]},{"title":"References","description":"Protocol specification, transaction types, ledger entries, and API methods.","includeFiles":["docs/references/**/*.*"],"excludeFiles":["docs/references/xrp-api.md","docs/references/data-api.md","docs/references/protocol/index.md","docs/references/protocol/ledger-data/ledger-entry-types/index.md","docs/references/protocol/transactions/index.md","docs/references/protocol/transactions/types/index.md","docs/references/http-websocket-apis/api-conventions/index.md","docs/references/http-websocket-apis/public-api-methods/*/index.md","docs/references/http-websocket-apis/admin-api-methods/*/index.md"]},{"title":"Infrastructure","description":"Install, configure, and troubleshoot rippled and Clio servers.","includeFiles":["docs/infrastructure/**/*.*"],"excludeFiles":["docs/infrastructure/index.md","docs/infrastructure/*/index.md","docs/infrastructure/installation/build-run-rippled-in-reporting-mode.md","docs/infrastructure/configuration/data-retention/index.md","docs/infrastructure/configuration/server-modes/index.md"]},{"title":"Blog (2023+)","description":"Recent XRPL Blog posts (showing 2023 and newer).","includeFiles":["blog/2023/**/*.*","blog/2024/**/*.*","blog/2025/**/*.*","blog/2026/**/*.*"],"excludeFiles":[]},{"title":"Resources","description":"Developer resources and contribution guidelines.","includeFiles":["resources/**/*.*"],"excludeFiles":["resources/index.md"]}],"excludeFiles":[]}},"dynamicMarkdocComponents":[],"compilationErrors":[],"ast":{"$$mdtype":"Tag","name":"article","attributes":{},"children":[{"$$mdtype":"Tag","name":"Heading","attributes":{"level":1,"id":"payments-escrow--agentic-patterns","__idx":0},"children":["Payments, Escrow & Agentic Patterns"]},{"$$mdtype":"Tag","name":"blockquote","attributes":{},"children":[{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Architecture note — who submits."]}," In the two-skill setup, this Payments"," ","skill ",{"$$mdtype":"Tag","name":"em","attributes":{},"children":["constructs"]}," the transaction object; the ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["XRPL Agent Wallet skill"]}," ","owns the final steps — autofill, human preview, local signing, and"," ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["submitAndWait"]},". The ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["submit_and_wait"]}," / ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["submitAndWait"]}," (and the ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["autofill"]}," ","in the simulate example) shown below are included so each snippet runs"," ","standalone for a developer exploring the API. In the agentic flow, stop at"," ","object construction and hand the object to the Wallet skill instead of calling"," ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["submit_and_wait"]}," yourself. See the Wallet skill for the signing ceremony."]}]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"the-build---hand-off-flow","__idx":1},"children":["The build -> hand-off flow"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["This is the shape of every agentic payment in the two-skill setup: this skill"," ","builds the object and stops; the Wallet skill does everything after."]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"python","header":{"controls":{"copy":{}}},"source":"# 1. This skill: construct the transaction object.\nfrom xrpl.models.transactions import Payment\nfrom xrpl.utils import xrp_to_drops\n\npayment = Payment(\n    account=wallet.address,\n    destination=\"rDestinationAddress\",\n    amount=xrp_to_drops(25),\n    source_tag=AGENT_SOURCE_TAG,\n)\n\n# 2. Hand `payment` to the XRPL Agent Wallet skill, which runs the ceremony:\n#       autofill -> preview to the human -> confirm -> sign locally -> submitAndWait\n#    Do NOT autofill, sign, or submit here.\n","lang":"python"},"children":[]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"typescript","header":{"controls":{"copy":{}}},"source":"// 1. This skill: construct the transaction object.\nimport { Payment, xrpToDrops } from \"xrpl\";\n\nconst payment: Payment = {\n  TransactionType: \"Payment\",\n  Account: wallet.address,\n  Destination: \"rDestinationAddress\",\n  Amount: xrpToDrops(\"25\"),\n  SourceTag: AGENT_SOURCE_TAG,\n};\n\n// 2. Hand `payment` to the XRPL Agent Wallet skill, which autofills, previews,\n//    signs locally, and submitAndWaits. Do NOT autofill, sign, or submit here.\n","lang":"typescript"},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"account-setup","__idx":2},"children":["Account Setup"]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"generate-and-fund-a-testnet-wallet","__idx":3},"children":["Generate and fund a testnet wallet"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"python","header":{"controls":{"copy":{}}},"source":"from xrpl.clients import JsonRpcClient\nfrom xrpl.wallet import generate_faucet_wallet\n\nTESTNET_URL = \"https://s.altnet.rippletest.net:51234\"\nclient = JsonRpcClient(TESTNET_URL)\n\nwallet = generate_faucet_wallet(client, debug=True)\nprint(f\"Address : {wallet.address}\")\n# Persist wallet.seed to a secret store (e.g. .env / KMS) — never print or log it.\n# Wallet generation and key handling belong to the XRPL Agent Wallet skill.\n","lang":"python"},"children":[]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"typescript","header":{"controls":{"copy":{}}},"source":"import { Client, Wallet } from \"xrpl\";\n\nconst client = new Client(\"wss://s.altnet.rippletest.net:51233\");\nawait client.connect();\nconst { wallet } = await client.fundWallet();\nconsole.log(\"Address:\", wallet.address);\n// Persist wallet.seed to a secret store (e.g. .env / KMS) — never print or log it.\n// Wallet generation and key handling belong to the XRPL Agent Wallet skill.\nawait client.disconnect();\n","lang":"typescript"},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"load-a-wallet-from-an-environment-variable","__idx":4},"children":["Load a wallet from an environment variable"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"python","header":{"controls":{"copy":{}}},"source":"import os\nfrom xrpl.wallet import Wallet\n\nwallet = Wallet.from_seed(os.environ[\"XRPL_SEED\"])\n","lang":"python"},"children":[]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"typescript","header":{"controls":{"copy":{}}},"source":"const wallet = Wallet.fromSeed(process.env.XRPL_SEED!);\n","lang":"typescript"},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"check-balance","__idx":5},"children":["Check balance"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"python","header":{"controls":{"copy":{}}},"source":"from xrpl.models.requests import AccountInfo\nfrom xrpl.utils import drops_to_xrp\n\nresponse = client.request(AccountInfo(account=wallet.address, ledger_index=\"validated\"))\nbalance_xrp = drops_to_xrp(response.result[\"account_data\"][\"Balance\"])\nprint(f\"Balance: {balance_xrp} XRP\")\n","lang":"python"},"children":[]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"typescript","header":{"controls":{"copy":{}}},"source":"const response = await client.request({\n  command: \"account_info\",\n  account: wallet.address,\n  ledger_index: \"validated\",\n});\nconst balanceXRP = Number(response.result.account_data.Balance) / 1_000_000;\nconsole.log(\"Balance:\", balanceXRP, \"XRP\");\n","lang":"typescript"},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"get-transaction-status","__idx":6},"children":["Get transaction status"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"python","header":{"controls":{"copy":{}}},"source":"from xrpl.models.requests import Tx\n\nresponse = client.request(Tx(transaction=\"<tx_hash>\"))\nresult = response.result[\"meta\"][\"TransactionResult\"]\n# tesSUCCESS = confirmed; anything else = failed\n","lang":"python"},"children":[]},{"$$mdtype":"Tag","name":"hr","attributes":{},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"xrp-payments","__idx":7},"children":["XRP Payments"]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"direct-payment","__idx":8},"children":["Direct payment"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"python","header":{"controls":{"copy":{}}},"source":"from xrpl.models.transactions import Payment\nfrom xrpl.utils import xrp_to_drops\nfrom xrpl.transaction import submit_and_wait\n\npayment = Payment(\n    account=wallet.address,\n    destination=\"rDestinationAddress\",\n    amount=xrp_to_drops(25),        # always use xrp_to_drops — never raw floats\n    source_tag=AGENT_SOURCE_TAG,    # tag every agentic transaction\n)\nresponse = submit_and_wait(payment, client, wallet)\nprint(f\"Result : {response.result['meta']['TransactionResult']}\")\nprint(f\"Hash   : {response.result['hash']}\")\n","lang":"python"},"children":[]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"typescript","header":{"controls":{"copy":{}}},"source":"import { Payment, xrpToDrops } from \"xrpl\";\n\nconst payment: Payment = {\n  TransactionType: \"Payment\",\n  Account: wallet.address,\n  Destination: \"rDestinationAddress\",\n  Amount: xrpToDrops(\"25\"),\n  SourceTag: AGENT_SOURCE_TAG,\n};\nconst response = await client.submitAndWait(payment, { wallet });\nconsole.log(response.result.meta?.TransactionResult);\n","lang":"typescript"},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"with-a-destination-tag","__idx":9},"children":["With a destination tag"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Use ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["destination_tag"]}," when the destination is a hosted wallet (exchange, payment processor) that routes by tag."]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"python","header":{"controls":{"copy":{}}},"source":"payment = Payment(\n    account=wallet.address,\n    destination=\"rExchangeAddress\",\n    amount=xrp_to_drops(100),\n    destination_tag=987654,   # required if destination has asfRequireDestTag set\n    source_tag=AGENT_SOURCE_TAG,\n)\n","lang":"python"},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":4,"id":"check-whether-a-destination-requires-a-tag","__idx":10},"children":["Check whether a destination requires a tag"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Sending to an account that has ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["asfRequireDestTag"]}," set without a"," ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["destination_tag"]}," fails with ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["tecDST_TAG_NEEDED"]},". Detect it first by inspecting"," ","the destination's account-root flags, and require a tag before building the"," ","payment."]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"python","header":{"controls":{"copy":{}}},"source":"from xrpl.models.requests import AccountInfo\n\nLSF_REQUIRE_DEST_TAG = 0x00020000  # account-root flag: destination tag required\n\ndef requires_dest_tag(address: str) -> bool:\n    info = client.request(AccountInfo(account=address, ledger_index=\"validated\"))\n    flags = info.result[\"account_data\"].get(\"Flags\", 0)\n    return bool(flags & LSF_REQUIRE_DEST_TAG)\n\nif requires_dest_tag(\"rExchangeAddress\"):\n    # Don't build the payment without a destination_tag, or it fails with\n    # tecDST_TAG_NEEDED. Get the tag from the recipient (exchange memo line, etc).\n    ...\n","lang":"python"},"children":[]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"typescript","header":{"controls":{"copy":{}}},"source":"import { AccountRootFlags } from \"xrpl\";\n\nasync function requiresDestTag(address: string): Promise<boolean> {\n  const info = await client.request({\n    command: \"account_info\",\n    account: address,\n    ledger_index: \"validated\",\n  });\n  const flags = info.result.account_data.Flags ?? 0;\n  return (flags & AccountRootFlags.lsfRequireDestTag) !== 0;\n}\n","lang":"typescript"},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"partial-payments","__idx":11},"children":["Partial payments"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Partial payments deliver ",{"$$mdtype":"Tag","name":"em","attributes":{},"children":["up to"]}," the specified amount. Always read ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["meta.delivered_amount"]}," — not ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["Amount"]}," — to know what actually arrived."]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"python","header":{"controls":{"copy":{}}},"source":"from xrpl.models.transactions import Payment\nfrom xrpl.models.transactions.payment import PaymentFlag\n\npayment = Payment(\n    account=wallet.address,\n    destination=\"rDestinationAddress\",\n    amount=xrp_to_drops(100),        # maximum to deliver\n    send_max=xrp_to_drops(100),\n    flags=PaymentFlag.TF_PARTIAL_PAYMENT,\n    source_tag=AGENT_SOURCE_TAG,\n)\nresponse = submit_and_wait(payment, client, wallet)\ndelivered = response.result[\"meta\"][\"delivered_amount\"]\nprint(f\"Actually delivered: {drops_to_xrp(delivered)} XRP\")\n","lang":"python"},"children":[]},{"$$mdtype":"Tag","name":"blockquote","attributes":{},"children":[{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Security:"]}," When ",{"$$mdtype":"Tag","name":"em","attributes":{},"children":["receiving"]}," payments, always check ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["meta.delivered_amount"]},", not ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["Amount"]},". They differ for partial payments and cross-currency payments."]}]},{"$$mdtype":"Tag","name":"hr","attributes":{},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"rlusd-payments","__idx":12},"children":["RLUSD Payments"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["RLUSD is Ripple's USD stablecoin on the XRP Ledger. Use it for dollar-denominated agent payments."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["RLUSD constants:"]}]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"python","header":{"controls":{"copy":{}}},"source":"# Hex-encoded currency code — \"RLUSD\" in ASCII padded to 40 hex chars\nRLUSD_CURRENCY = \"524C555344000000000000000000000000000000\"\n\n# Issuer addresses — confirm before production use\nRLUSD_ISSUER_TESTNET = \"rQhWct2fv4Vc4KRjRgMrxa8xPN9Zx9iLKV\"\nRLUSD_ISSUER_MAINNET = \"rMxCKbEDwqr76QuheSUMdEGf4B9xJ8m5De\" # Verify at [docs.ripple.com](https://docs.ripple.com/products/stablecoin/developer-resources/rlusd-on-the-xrpl)\n","lang":"python"},"children":[]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"typescript","header":{"controls":{"copy":{}}},"source":"// Hex-encoded currency code — \"RLUSD\" in ASCII padded to 40 hex chars\nconst RLUSD_CURRENCY = \"524C555344000000000000000000000000000000\";\n\n// Issuer addresses — confirm before production use\nconst RLUSD_ISSUER_TESTNET = \"rQhWct2fv4Vc4KRjRgMrxa8xPN9Zx9iLKV\";\nconst RLUSD_ISSUER_MAINNET = \"rMxCKbEDwqr76QuheSUMdEGf4B9xJ8m5De\"; // Verify at [docs.ripple.com](https://docs.ripple.com/products/stablecoin/developer-resources/rlusd-on-the-xrpl)\n","lang":"typescript"},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"step-1-set-up-the-trust-line-one-time-per-wallet","__idx":13},"children":["Step 1: Set up the trust line (one-time per wallet)"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["A wallet must set a trust line to the RLUSD issuer before it can hold or receive RLUSD. Do this once per wallet. The transaction fails with ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["tecNO_LINE"]}," if you skip it."]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"python","header":{"controls":{"copy":{}}},"source":"from xrpl.models.transactions import TrustSet\nfrom xrpl.models.amounts import IssuedCurrencyAmount\n\ntrust_set = TrustSet(\n    account=wallet.address,\n    limit_amount=IssuedCurrencyAmount(\n        currency=RLUSD_CURRENCY,\n        issuer=RLUSD_ISSUER_TESTNET,\n        value=\"10000\",   # max RLUSD this wallet will hold\n    ),\n)\nresult = submit_and_wait(trust_set, client, wallet)\nprint(f\"Trust line: {result.result['meta']['TransactionResult']}\")\n","lang":"python"},"children":[]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"typescript","header":{"controls":{"copy":{}}},"source":"import { TrustSet } from \"xrpl\";\n\nconst trustSet: TrustSet = {\n  TransactionType: \"TrustSet\",\n  Account: wallet.address,\n  LimitAmount: {\n    currency: RLUSD_CURRENCY,\n    issuer: RLUSD_ISSUER_TESTNET,\n    value: \"10000\",\n  },\n};\nawait client.submitAndWait(trustSet, { wallet });\n","lang":"typescript"},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"step-2-send-rlusd","__idx":14},"children":["Step 2: Send RLUSD"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"python","header":{"controls":{"copy":{}}},"source":"from xrpl.models.transactions import Payment\nfrom xrpl.models.amounts import IssuedCurrencyAmount\n\npayment = Payment(\n    account=wallet.address,\n    destination=\"rDestinationAddress\",\n    amount=IssuedCurrencyAmount(\n        currency=RLUSD_CURRENCY,\n        issuer=RLUSD_ISSUER_TESTNET,\n        value=\"250\",     # 250 RLUSD\n    ),\n    source_tag=AGENT_SOURCE_TAG,\n)\nresponse = submit_and_wait(payment, client, wallet)\nprint(f\"Result : {response.result['meta']['TransactionResult']}\")\nprint(f\"Hash   : {response.result['hash']}\")\n","lang":"python"},"children":[]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"typescript","header":{"controls":{"copy":{}}},"source":"const payment: Payment = {\n  TransactionType: \"Payment\",\n  Account: wallet.address,\n  Destination: \"rDestinationAddress\",\n  Amount: {\n    currency: RLUSD_CURRENCY,\n    issuer: RLUSD_ISSUER_TESTNET,\n    value: \"250\",\n  },\n  SourceTag: AGENT_SOURCE_TAG,\n};\nconst response = await client.submitAndWait(payment, { wallet });\n","lang":"typescript"},"children":[]},{"$$mdtype":"Tag","name":"blockquote","attributes":{},"children":[{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Note:"]}," The destination wallet must also have an RLUSD trust line, or the payment fails with ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["tecNO_LINE"]},". The exception is the issuer itself."]}]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"check-rlusd-balance","__idx":15},"children":["Check RLUSD balance"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"python","header":{"controls":{"copy":{}}},"source":"from xrpl.models.requests import AccountLines\n\nresponse = client.request(AccountLines(\n    account=wallet.address,\n    ledger_index=\"validated\",\n))\nfor line in response.result[\"lines\"]:\n    if line[\"currency\"] == RLUSD_CURRENCY and line[\"account\"] == RLUSD_ISSUER_TESTNET:\n        print(f\"RLUSD balance: {line['balance']}\")\n","lang":"python"},"children":[]},{"$$mdtype":"Tag","name":"hr","attributes":{},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"generic-iou-token-payments","__idx":16},"children":["Generic IOU Token Payments"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["The same pattern applies for any IOU token, not just RLUSD. Replace the currency code and issuer."]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"python","header":{"controls":{"copy":{}}},"source":"payment = Payment(\n    account=wallet.address,\n    destination=\"rDestinationAddress\",\n    amount=IssuedCurrencyAmount(\n        currency=\"USD\",          # 3-char ASCII or 40-char hex\n        issuer=\"rIssuerAddress\",\n        value=\"100\",\n    ),\n    source_tag=AGENT_SOURCE_TAG,\n)\n","lang":"python"},"children":[]},{"$$mdtype":"Tag","name":"hr","attributes":{},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"cross-currency-payments","__idx":17},"children":["Cross-Currency Payments"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Send one currency; the destination receives another. The XRP Ledger's built-in DEX handles the conversion atomically — no external swap or bridge needed."]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"python","header":{"controls":{"copy":{}}},"source":"from xrpl.models.transactions import Payment\nfrom xrpl.models.amounts import IssuedCurrencyAmount\nfrom xrpl.utils import xrp_to_drops\n\n# Spend up to 15 XRP; destination receives exactly 10 RLUSD.\npayment = Payment(\n    account=wallet.address,\n    destination=\"rDestinationAddress\",\n    amount=IssuedCurrencyAmount(        # what the destination receives\n        currency=RLUSD_CURRENCY,\n        issuer=RLUSD_ISSUER_TESTNET,\n        value=\"10\",\n    ),\n    send_max=xrp_to_drops(15),          # maximum you'll spend\n    source_tag=AGENT_SOURCE_TAG,\n)\nresponse = submit_and_wait(payment, client, wallet)\n","lang":"python"},"children":[]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"typescript","header":{"controls":{"copy":{}}},"source":"const payment: Payment = {\n  TransactionType: \"Payment\",\n  Account: wallet.address,\n  Destination: \"rDestinationAddress\",\n  Amount: { currency: RLUSD_CURRENCY, issuer: RLUSD_ISSUER_TESTNET, value: \"10\" },\n  SendMax: xrpToDrops(\"15\"),\n  SourceTag: AGENT_SOURCE_TAG,\n};\n","lang":"typescript"},"children":[]},{"$$mdtype":"Tag","name":"blockquote","attributes":{},"children":[{"$$mdtype":"Tag","name":"p","attributes":{},"children":["When reading cross-currency payment results, check ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["meta.delivered_amount"]}," for the actual amount delivered."]}]},{"$$mdtype":"Tag","name":"hr","attributes":{},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"escrow","__idx":18},"children":["Escrow"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Escrows lock XRP until a time condition or cryptographic condition is met. Useful for staged agent payments and conditional disbursements."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"time-based-escrow","__idx":19},"children":["Time-based escrow"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"python","header":{"controls":{"copy":{}}},"source":"import time\nfrom xrpl.models.transactions import EscrowCreate, EscrowFinish, EscrowCancel\n\nRIPPLE_EPOCH_OFFSET = 946684800  # seconds between Unix epoch and Ripple epoch\n\ndef unix_to_ripple_time(unix_ts: float) -> int:\n    return int(unix_ts) - RIPPLE_EPOCH_OFFSET\n\n# Create: lock 50 XRP, claimable after 24 h, cancellable after 7 days\nescrow_create = EscrowCreate(\n    account=wallet.address,\n    destination=\"rRecipientAddress\",\n    amount=xrp_to_drops(50),\n    finish_after=unix_to_ripple_time(time.time() + 86400),    # 24 hours\n    cancel_after=unix_to_ripple_time(time.time() + 604800),   # 7 days\n)\nresult = submit_and_wait(escrow_create, client, wallet)\nescrow_sequence = result.result[\"Sequence\"]\n\n# Finish: recipient (or anyone) claims after FinishAfter\nescrow_finish = EscrowFinish(\n    account=\"rRecipientAddress\",\n    owner=wallet.address,\n    offer_sequence=escrow_sequence,\n)\nsubmit_and_wait(escrow_finish, client, recipient_wallet)\n\n# Cancel: sender reclaims after CancelAfter\nescrow_cancel = EscrowCancel(\n    account=wallet.address,\n    owner=wallet.address,\n    offer_sequence=escrow_sequence,\n)\nsubmit_and_wait(escrow_cancel, client, wallet)\n","lang":"python"},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"conditional-escrow-crypto-conditions","__idx":20},"children":["Conditional escrow (crypto-conditions)"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"python","header":{"controls":{"copy":{}}},"source":"from xrpl.models.transactions import EscrowCreate, EscrowFinish\n\nescrow_create = EscrowCreate(\n    account=wallet.address,\n    destination=\"rRecipientAddress\",\n    amount=xrp_to_drops(50),\n    condition=\"A0258020...\",   # PREIMAGE-SHA-256 hex condition\n    cancel_after=unix_to_ripple_time(time.time() + 604800),\n)\nresult = submit_and_wait(escrow_create, client, wallet)\n\nescrow_finish = EscrowFinish(\n    account=\"rRecipientAddress\",\n    owner=wallet.address,\n    offer_sequence=result.result[\"Sequence\"],\n    condition=\"A0258020...\",\n    fulfillment=\"A0228020...\",   # preimage that satisfies the condition\n)\nsubmit_and_wait(escrow_finish, client, recipient_wallet)\n","lang":"python"},"children":[]},{"$$mdtype":"Tag","name":"hr","attributes":{},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"agentic-best-practices","__idx":21},"children":["Agentic Best Practices"]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"sourcetag--tracking-agent-generated-volume","__idx":22},"children":["SourceTag — tracking agent-generated volume"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Set a consistent 32-bit unsigned integer on every transaction your agent submits. This lets you filter on-chain volume by agent, report on agentic activity, and separate it from human-initiated transactions."]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"python","header":{"controls":{"copy":{}}},"source":"AGENT_SOURCE_TAG = 20260530  \n\npayment = Payment(\n    account=wallet.address,\n    destination=\"rDestinationAddress\",\n    amount=xrp_to_drops(10),\n    source_tag=AGENT_SOURCE_TAG,\n)\n","lang":"python"},"children":[]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"typescript","header":{"controls":{"copy":{}}},"source":"const payment: Payment = {\n  TransactionType: \"Payment\",\n  Account: wallet.address,\n  Destination: \"rDestinationAddress\",\n  Amount: xrpToDrops(\"10\"),\n  SourceTag: AGENT_SOURCE_TAG,\n};\n","lang":"typescript"},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"memos--on-chain-audit-trail","__idx":23},"children":["Memos — on-chain audit trail"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Embed structured metadata in every agent transaction to correlate on-chain activity with your application logs. Memo values must be hex-encoded."]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"python","header":{"controls":{"copy":{}}},"source":"import json, base64\nfrom xrpl.models.transactions.transaction import Memo\n\ndef build_memo(agent_id: str, session_id: str, action: str, task_id: str) -> Memo:\n    payload = json.dumps({\n        \"agent_id\":   agent_id,\n        \"session_id\": session_id,\n        \"action\":     action,\n        \"task_id\":    task_id,\n    }, separators=(\",\", \":\"))\n    return Memo(memo_data=base64.b16encode(payload.encode()).decode())\n\npayment = Payment(\n    account=wallet.address,\n    destination=\"rDestinationAddress\",\n    amount=xrp_to_drops(25),\n    source_tag=AGENT_SOURCE_TAG,\n    memos=[build_memo(\"invoice-agent-v1\", \"sess-abc123\", \"pay_invoice\", \"inv-00789\")],\n)\nresponse = submit_and_wait(payment, client, wallet)\n","lang":"python"},"children":[]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"typescript","header":{"controls":{"copy":{}}},"source":"function buildMemo(agentId: string, sessionId: string, action: string, taskId: string) {\n  const payload = JSON.stringify({ agent_id: agentId, session_id: sessionId, action, task_id: taskId });\n  return { Memo: { MemoData: Buffer.from(payload).toString(\"hex\").toUpperCase() } };\n}\n\nconst payment: Payment = {\n  TransactionType: \"Payment\",\n  Account: wallet.address,\n  Destination: \"rDestinationAddress\",\n  Amount: xrpToDrops(\"25\"),\n  SourceTag: AGENT_SOURCE_TAG,\n  Memos: [buildMemo(\"invoice-agent-v1\", \"sess-abc123\", \"pay_invoice\", \"inv-00789\")],\n};\n","lang":"typescript"},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"decoding-memos-for-log-correlation","__idx":24},"children":["Decoding memos (for log correlation)"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"python","header":{"controls":{"copy":{}}},"source":"import json, binascii\n\ndef decode_memo(memo_hex: str) -> dict:\n    return json.loads(binascii.unhexlify(memo_hex).decode(\"utf-8\"))\n\n# From a fetched transaction:\ntx_memos = response.result.get(\"Memos\", [])\nfor entry in tx_memos:\n    data = decode_memo(entry[\"Memo\"][\"MemoData\"])\n    print(data)  # {\"agent_id\": ..., \"session_id\": ..., \"action\": ..., \"task_id\": ...}\n","lang":"python"},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"websocket-monitoring--trigger-agent-steps-on-incoming-transactions","__idx":25},"children":["WebSocket monitoring — trigger agent steps on incoming transactions"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"python","header":{"controls":{"copy":{}}},"source":"import asyncio, json, websockets\n\nTESTNET_WS = \"wss://s.altnet.rippletest.net:51233\"\n\nasync def monitor_account(address: str):\n    async with websockets.connect(TESTNET_WS) as ws:\n        await ws.send(json.dumps({\"command\": \"subscribe\", \"accounts\": [address]}))\n        async for message in ws:\n            event = json.loads(message)\n            if event.get(\"type\") == \"transaction\":\n                tx = event.get(\"transaction\", {})\n                meta = event.get(\"meta\", {})\n                if meta.get(\"TransactionResult\") == \"tesSUCCESS\":\n                    print(f\"Confirmed: {tx.get('TransactionType')} | {tx.get('hash')}\")\n                    # trigger next agent step here\n\nasyncio.run(monitor_account(wallet.address))\n","lang":"python"},"children":[]},{"$$mdtype":"Tag","name":"hr","attributes":{},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"spending-controls-institutional--production","__idx":26},"children":["Spending Controls (Institutional / Production)"]},{"$$mdtype":"Tag","name":"div","attributes":{"className":"md-table-wrapper"},"children":[{"$$mdtype":"Tag","name":"table","attributes":{"className":"md"},"children":[{"$$mdtype":"Tag","name":"thead","attributes":{},"children":[{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"th","attributes":{"data-label":"Control"},"children":["Control"]},{"$$mdtype":"Tag","name":"th","attributes":{"data-label":"How to set"},"children":["How to set"]},{"$$mdtype":"Tag","name":"th","attributes":{"data-label":"What it does"},"children":["What it does"]}]}]},{"$$mdtype":"Tag","name":"tbody","attributes":{},"children":[{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Escrow"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["EscrowCreate"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Locks XRP until a time or crypto condition — staged disbursements"]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Multi-sig"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["SignerListSet"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Requires M-of-N keys — human-in-the-loop for high-value transfers"]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["DepositAuth"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["AccountSet"]}," with ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["asfDepositAuth"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Blocks unsolicited incoming payments to the agent wallet"]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Trust lines"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["TrustSet"]}," with low limit"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Caps token exposure to a defined maximum"]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Freeze"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Issuer sets ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["TrustSet"]}," freeze flag"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Issuer can freeze an individual trust line"]}]}]}]}]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["DepositAuth example:"]}]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"python","header":{"controls":{"copy":{}}},"source":"from xrpl.models.transactions import AccountSet, AccountSetAsfFlag\n\naccount_set = AccountSet(\n    account=wallet.address,\n    set_flag=AccountSetAsfFlag.ASF_DEPOSIT_AUTH,\n)\nsubmit_and_wait(account_set, client, wallet)\n# Now only pre-authorized senders can pay this wallet\n","lang":"python"},"children":[]},{"$$mdtype":"Tag","name":"hr","attributes":{},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"simulate-before-submit","__idx":27},"children":["Simulate Before Submit"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Use ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["simulate"]}," to dry-run a transaction before spending fees. The ledger evaluates the transaction and returns what the result ",{"$$mdtype":"Tag","name":"em","attributes":{},"children":["would"]}," be — including which errors it would hit — without actually executing it or charging a fee."]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"python","header":{"controls":{"copy":{}}},"source":"from xrpl.models.requests import Simulate\nfrom xrpl.transaction import autofill, submit_and_wait\n\npayment = Payment(\n    account=wallet.address,\n    destination=\"rDestinationAddress\",\n    amount=xrp_to_drops(25),\n    source_tag=AGENT_SOURCE_TAG,\n)\nfilled = autofill(payment, client)\n\n# Simulate the autofilled (unsigned) transaction — no fee charged, no ledger state changed\nsim_response = client.request(Simulate(transaction=filled))\nsim_result = sim_response.result[\"meta\"][\"TransactionResult\"]\n\nif sim_result != \"tesSUCCESS\":\n    raise RuntimeError(f\"Simulation failed: {sim_result} — fix before submitting\")\n\n# Safe to submit — submit_and_wait handles signing internally\nresponse = submit_and_wait(payment, client, wallet)\n","lang":"python"},"children":[]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"typescript","header":{"controls":{"copy":{}}},"source":"const payment: Payment = {\n  TransactionType: \"Payment\",\n  Account: wallet.address,\n  Destination: \"rDestinationAddress\",\n  Amount: xrpToDrops(\"25\"),\n  SourceTag: AGENT_SOURCE_TAG,\n};\n\nconst filled = await client.autofill(payment);\n\nconst simResponse = await client.request({\n  command: \"simulate\",\n  tx_json: filled,\n});\nconst simResult = simResponse.result.meta?.TransactionResult;\n\nif (simResult !== \"tesSUCCESS\") {\n  throw new Error(`Simulation failed: ${simResult}`);\n}\n\nconst response = await client.submitAndWait(payment, { wallet });\n","lang":"typescript"},"children":[]},{"$$mdtype":"Tag","name":"blockquote","attributes":{},"children":[{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["When to skip simulation:"]}," High-frequency agents with stable, pre-validated payment paths can skip simulation for speed. Always simulate during development and when building new payment flows."]}]},{"$$mdtype":"Tag","name":"hr","attributes":{},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"transaction-result-codes","__idx":28},"children":["Transaction Result Codes"]},{"$$mdtype":"Tag","name":"div","attributes":{"className":"md-table-wrapper"},"children":[{"$$mdtype":"Tag","name":"table","attributes":{"className":"md"},"children":[{"$$mdtype":"Tag","name":"thead","attributes":{},"children":[{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"th","attributes":{"data-label":"Prefix"},"children":["Prefix"]},{"$$mdtype":"Tag","name":"th","attributes":{"data-label":"Fee charged?"},"children":["Fee charged?"]},{"$$mdtype":"Tag","name":"th","attributes":{"data-label":"Meaning"},"children":["Meaning"]},{"$$mdtype":"Tag","name":"th","attributes":{"data-label":"Common examples"},"children":["Common examples"]}]}]},{"$$mdtype":"Tag","name":"tbody","attributes":{},"children":[{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["tesSUCCESS"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Yes"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Transaction confirmed"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["—"]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["tec..."]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Yes"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Executed but failed"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["tecNO_LINE"]}," (no trust line), ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["tecINSUF_RESERVE_LINE"]}," (not enough XRP for reserve), ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["tecUNFUNDED_PAYMENT"]}," (insufficient balance)"]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["tef..."]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["No"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Failed before executing"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["tefBAD_AUTH"]}," (wrong signing key), ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["tefPAST_SEQ"]}," (sequence already used)"]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["tel..."]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["No"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Local error (not broadcast)"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["telINSUF_FEE_P"]}," (fee too low)"]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["tem..."]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["No"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Malformed transaction"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["temBAD_AMOUNT"]},", ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["temBAD_CURRENCY"]}]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["ter..."]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["No"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Retry — transient error"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["terQUEUED"]}," (transaction queued)"]}]}]}]}]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Checking results in code:"]}]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"python","header":{"controls":{"copy":{}}},"source":"result_code = response.result[\"meta\"][\"TransactionResult\"]\nif result_code == \"tesSUCCESS\":\n    tx_hash = response.result[\"hash\"]\nelif result_code.startswith(\"tec\"):\n    # Fee was charged. Log and handle gracefully.\n    raise RuntimeError(f\"Transaction failed (fee charged): {result_code}\")\nelse:\n    # No fee charged. Safe to retry after fixing the issue.\n    raise RuntimeError(f\"Transaction rejected: {result_code}\")\n","lang":"python"},"children":[]},{"$$mdtype":"Tag","name":"hr","attributes":{},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"reserve-requirements","__idx":29},"children":["Reserve Requirements"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Every XRPL account must maintain a minimum XRP balance (the base reserve) plus an incremental reserve per ledger object it owns. Agents must account for this or risk ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["tecINSUF_RESERVE_LINE"]}," and ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["tecUNFUNDED_PAYMENT"]}," errors."]},{"$$mdtype":"Tag","name":"div","attributes":{"className":"md-table-wrapper"},"children":[{"$$mdtype":"Tag","name":"table","attributes":{"className":"md"},"children":[{"$$mdtype":"Tag","name":"thead","attributes":{},"children":[{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"th","attributes":{"data-label":"Object"},"children":["Object"]},{"$$mdtype":"Tag","name":"th","attributes":{"data-label":"Reserve cost"},"children":["Reserve cost"]}]}]},{"$$mdtype":"Tag","name":"tbody","attributes":{},"children":[{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Account activation"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["1 XRP base reserve"]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Each trust line"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["+0.2 XRP owner reserve"]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Each escrow"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["+0.2 XRP owner reserve"]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Each open offer (DEX)"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["+0.2 XRP owner reserve"]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Each payment channel"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["+0.2 XRP owner reserve"]}]}]}]}]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"python","header":{"controls":{"copy":{}}},"source":"# Check spendable balance (total minus locked reserves)\nresponse = client.request(AccountInfo(account=wallet.address, ledger_index=\"validated\"))\ndata = response.result[\"account_data\"]\ntotal_drops = int(data[\"Balance\"])\nowner_count = int(data[\"OwnerCount\"])\n\nBASE_RESERVE_DROPS   = 1_000_000   # 1 XRP\nOWNER_RESERVE_DROPS  =  200_000   # 0.2 XRP per object\n\nlocked_drops    = BASE_RESERVE_DROPS + (owner_count * OWNER_RESERVE_DROPS)\nspendable_drops = total_drops - locked_drops\nprint(f\"Spendable: {drops_to_xrp(str(spendable_drops))} XRP\")\n","lang":"python"},"children":[]},{"$$mdtype":"Tag","name":"hr","attributes":{},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"testnet---mainnet-checklist","__idx":30},"children":["Testnet -> Mainnet Checklist"]},{"$$mdtype":"Tag","name":"ul","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"input","attributes":{"checked":false,"type":"checkbox","readOnly":true},"children":[]}," Replace testnet URL with ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["https://xrplcluster.com"]}," (RPC) or ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["wss://xrplcluster.com"]}," (WS)"]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"input","attributes":{"checked":false,"type":"checkbox","readOnly":true},"children":[]}," Use a funded mainnet wallet — not a faucet wallet"]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"input","attributes":{"checked":false,"type":"checkbox","readOnly":true},"children":[]}," Confirm canonical RLUSD issuer address for mainnet"]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"input","attributes":{"checked":false,"type":"checkbox","readOnly":true},"children":[]}," Set ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["source_tag"]}," to your registered agent tag"]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"input","attributes":{"checked":false,"type":"checkbox","readOnly":true},"children":[]}," Move signing keys to KMS/HSM (AWS KMS, GCP KMS, HashiCorp Vault)"]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"input","attributes":{"checked":false,"type":"checkbox","readOnly":true},"children":[]}," Validate full agent behavior on testnet first — identical API surface, no real funds at risk"]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"input","attributes":{"checked":false,"type":"checkbox","readOnly":true},"children":[]}," Test edge cases: transaction expiry, insufficient funds, no trust line, destination requires tag"]}]},{"$$mdtype":"Tag","name":"hr","attributes":{},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"network-endpoints","__idx":31},"children":["Network Endpoints"]},{"$$mdtype":"Tag","name":"div","attributes":{"className":"md-table-wrapper"},"children":[{"$$mdtype":"Tag","name":"table","attributes":{"className":"md"},"children":[{"$$mdtype":"Tag","name":"thead","attributes":{},"children":[{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"th","attributes":{"data-label":"Network"},"children":["Network"]},{"$$mdtype":"Tag","name":"th","attributes":{"data-label":"RPC"},"children":["RPC"]},{"$$mdtype":"Tag","name":"th","attributes":{"data-label":"WebSocket"},"children":["WebSocket"]}]}]},{"$$mdtype":"Tag","name":"tbody","attributes":{},"children":[{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Testnet"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["https://s.altnet.rippletest.net:51234"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["wss://s.altnet.rippletest.net:51233"]}]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Mainnet"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["https://xrplcluster.com"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["wss://xrplcluster.com"]}]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Devnet"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["https://s.devnet.rippletest.net:51234"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["wss://s.devnet.rippletest.net:51233"]}]}]}]}]}]}]},"headings":[{"value":"Payments, Escrow & Agentic Patterns","id":"payments-escrow--agentic-patterns","depth":1},{"value":"The build -> hand-off flow","id":"the-build---hand-off-flow","depth":2},{"value":"Account Setup","id":"account-setup","depth":2},{"value":"Generate and fund a testnet wallet","id":"generate-and-fund-a-testnet-wallet","depth":3},{"value":"Load a wallet from an environment variable","id":"load-a-wallet-from-an-environment-variable","depth":3},{"value":"Check balance","id":"check-balance","depth":3},{"value":"Get transaction status","id":"get-transaction-status","depth":3},{"value":"XRP Payments","id":"xrp-payments","depth":2},{"value":"Direct payment","id":"direct-payment","depth":3},{"value":"With a destination tag","id":"with-a-destination-tag","depth":3},{"value":"Check whether a destination requires a tag","id":"check-whether-a-destination-requires-a-tag","depth":4},{"value":"Partial payments","id":"partial-payments","depth":3},{"value":"RLUSD Payments","id":"rlusd-payments","depth":2},{"value":"Step 1: Set up the trust line (one-time per wallet)","id":"step-1-set-up-the-trust-line-one-time-per-wallet","depth":3},{"value":"Step 2: Send RLUSD","id":"step-2-send-rlusd","depth":3},{"value":"Check RLUSD balance","id":"check-rlusd-balance","depth":3},{"value":"Generic IOU Token Payments","id":"generic-iou-token-payments","depth":2},{"value":"Cross-Currency Payments","id":"cross-currency-payments","depth":2},{"value":"Escrow","id":"escrow","depth":2},{"value":"Time-based escrow","id":"time-based-escrow","depth":3},{"value":"Conditional escrow (crypto-conditions)","id":"conditional-escrow-crypto-conditions","depth":3},{"value":"Agentic Best Practices","id":"agentic-best-practices","depth":2},{"value":"SourceTag — tracking agent-generated volume","id":"sourcetag--tracking-agent-generated-volume","depth":3},{"value":"Memos — on-chain audit trail","id":"memos--on-chain-audit-trail","depth":3},{"value":"Decoding memos (for log correlation)","id":"decoding-memos-for-log-correlation","depth":3},{"value":"WebSocket monitoring — trigger agent steps on incoming transactions","id":"websocket-monitoring--trigger-agent-steps-on-incoming-transactions","depth":3},{"value":"Spending Controls (Institutional / Production)","id":"spending-controls-institutional--production","depth":2},{"value":"Simulate Before Submit","id":"simulate-before-submit","depth":2},{"value":"Transaction Result Codes","id":"transaction-result-codes","depth":2},{"value":"Reserve Requirements","id":"reserve-requirements","depth":2},{"value":"Testnet -> Mainnet Checklist","id":"testnet---mainnet-checklist","depth":2},{"value":"Network Endpoints","id":"network-endpoints","depth":2}],"frontmatter":{"seo":{"title":"Payments, Escrow & Agentic Patterns"}},"editPage":{"to":"https://github.com/XRPLF/xrpl-dev-portal/tree/master/.claude/skills/xrpl-skills/xrpl-payments/references/payments.md"},"lastModified":"2026-06-10T05:05:24.000Z","pagePropGetterError":{"message":"","name":""}},"slug":"/.claude/skills/xrpl-skills/xrpl-payments/references/payments","userData":{"isAuthenticated":false,"teams":["anonymous"]},"isPublic":true}