{"templateId":"markdown","sharedDataIds":{"sidebar":"sidebar-products/wallet/sidebars.yaml"},"props":{"metadata":{"markdoc":{"tagList":["admonition"]},"type":"markdown"},"seo":{"title":"Policy best practices","description":"User guides, API reference, and support resources.","siteUrl":"https://docs.ripple.com/products/custody","lang":"en-US","llmstxt":{"hide":false,"sections":[{"title":"Table of contents","includeFiles":["**/*"],"excludeFiles":[]}],"excludeFiles":[]}},"dynamicMarkdocComponents":[],"compilationErrors":[],"ast":{"$$mdtype":"Tag","name":"article","attributes":{},"children":[{"$$mdtype":"Tag","name":"Heading","attributes":{"level":1,"id":"policy-best-practices","__idx":0},"children":["Policy best practices"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["This page provides recommended patterns for planning, configuring, and maintaining transaction policies."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"planning-your-policy-strategy","__idx":1},"children":["Planning your policy strategy"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Start restrictive, then expand."]}," Begin with conservative limits and increase them as you understand your transaction patterns. It's easier to raise limits than to recover from unauthorized transactions."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Layer your limits."]}," Combine multiple limit types for defense in depth:"]},{"$$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":"Limit type"},"children":["Limit type"]},{"$$mdtype":"Tag","name":"th","attributes":{"data-label":"Purpose"},"children":["Purpose"]}]}]},{"$$mdtype":"Tag","name":"tbody","attributes":{},"children":[{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Per transaction"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Prevents large single transactions"]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Rolling duration"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Prevents rapid accumulation of smaller transactions"]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Max total value"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Provides an absolute backstop"]}]}]}]}]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["For example, a hot wallet holding ETH might use three policies together:"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"json","header":{"controls":{"copy":{}}},"source":"{\"limitType\": \"PER_TX\", \"symbol\": \"ETH\", \"limitQty\": \"10\"}\n{\"limitType\": \"ROLLING_DURATION\", \"symbol\": \"ETH\", \"limitQty\": \"50\", \"duration\": \"86400s\"}\n{\"limitType\": \"CONSTANT\", \"symbol\": \"ETH\", \"limitQty\": \"500\"}\n","lang":"json"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["This ensures no single transaction exceeds 10 ETH, no more than 50 ETH leaves in any 24-hour period, and the wallet can never send more than 500 ETH total."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Enable approval groups."]}," Without approval groups, anyone with ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["keylimit:create"]}," API permissions can instantly enable policies. See ",{"$$mdtype":"Tag","name":"a","attributes":{"href":"/products/wallet/user-interface/security-controls/approvals"},"children":["Approvals"]}," to add human oversight."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Avoid common mistakes:"]}]},{"$$mdtype":"Tag","name":"ul","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Don't use zero or negative limits, they block all transactions or behave unpredictably."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Don't rely on a single high-limit policy without matchers—it provides minimal protection."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Don't rely solely on per-transaction limits—combine with rolling limits to catch accumulation attacks."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Don't leave assets without policies—transactions will fail with \"no key limits found\"."]}]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"configuration-examples","__idx":2},"children":["Configuration examples"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["The following examples show common policy configurations for different wallet types."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Hot wallet"]}," – Process many small transactions automatically with tight limits:"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"json","header":{"controls":{"copy":{}}},"source":"{\"limitType\": \"PER_TX\", \"symbol\": \"ETH\", \"limitQty\": \"1\"}\n{\"limitType\": \"ROLLING_DURATION\", \"symbol\": \"ETH\", \"limitQty\": \"10\", \"duration\": \"3600s\"}\n{\"limitType\": \"ROLLING_DURATION\", \"symbol\": \"ETH\", \"limitQty\": \"50\", \"duration\": \"86400s\"}\n","lang":"json"},"children":[]},{"$$mdtype":"Tag","name":"Admonition","attributes":{"type":"info","name":"Multiple rolling policies"},"children":[{"$$mdtype":"Tag","name":"p","attributes":{},"children":["You can create multiple rolling-duration policies with different windows by using different matchers. Without matchers, only one rolling-duration policy per asset is allowed."]}]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Cold storage"]}," – Hold long-term reserves with strict, manually-approved withdrawals. Use low per-transaction limits and combine with mobile-only MPC quorums and transaction approval groups."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Treasury wallet"]}," – Handle large transfers to trusted counterparties while restricting other destinations:"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"json","header":{"controls":{"copy":{}}},"source":"{\"limitType\": \"PER_TX\", \"symbol\": \"ETH\", \"limitQty\": \"1000\", \"matchers\": [{\"type\": \"COUNTERPARTY_ID\", \"value\": \"trusted-uuid\"}]}\n{\"limitType\": \"PER_TX\", \"symbol\": \"ETH\", \"limitQty\": \"10\"}\n","lang":"json"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Role-based limits"]}," – Use ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["USER"]}," matchers to set different limits for different team members:"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"json","header":{"controls":{"copy":{}}},"source":"{\"limitType\": \"PER_TX\", \"symbol\": \"ETH\", \"limitQty\": \"5\", \"matchers\": [{\"type\": \"USER\", \"value\": \"ops-user-uuid\"}]}\n{\"limitType\": \"PER_TX\", \"symbol\": \"ETH\", \"limitQty\": \"100\", \"matchers\": [{\"type\": \"USER\", \"value\": \"finance-user-uuid\"}]}\n","lang":"json"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["API credential isolation"]}," – Use ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["API_CREDENTIAL"]}," matchers to limit each integration's authority:"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"json","header":{"controls":{"copy":{}}},"source":"{\"limitType\": \"PER_TX\", \"symbol\": \"ETH\", \"limitQty\": \"1\", \"matchers\": [{\"type\": \"API_CREDENTIAL\", \"value\": \"trading-bot-uuid\"}]}\n{\"limitType\": \"ROLLING_DURATION\", \"symbol\": \"ETH\", \"limitQty\": \"100\", \"duration\": \"86400s\", \"matchers\": [{\"type\": \"API_CREDENTIAL\", \"value\": \"settlement-uuid\"}]}\n","lang":"json"},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"ongoing-operations","__idx":3},"children":["Ongoing operations"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Review policies regularly."]}," Schedule periodic reviews to check whether limits are still appropriate for current transaction volumes, whether unused policies should be removed, and whether new assets need policies."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Monitor transaction rejections."]}," Frequent rejections may indicate limits set too low, attempted unauthorized access, or misconfigured policies. Use the transaction list to filter by ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["REJECTED"]}," status and review the rejection reasons."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Document your policy strategy."]}," Record why each policy exists, who approved it, and when it was last reviewed. This helps during audits and when onboarding new team members."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Upgrade to scoped matchers."]}," If you have existing policies without matchers, you can add more granular policies alongside them. The more specific policy (with matchers) takes precedence when its conditions match:"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"json","header":{"controls":{"copy":{}}},"source":"{\"limitType\": \"PER_TX\", \"symbol\": \"ETH\", \"limitQty\": \"10\"}\n{\"limitType\": \"PER_TX\", \"symbol\": \"ETH\", \"limitQty\": \"100\", \"matchers\": [{\"type\": \"COUNTERPARTY_ID\", \"value\": \"trusted-uuid\"}]}\n","lang":"json"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Replace policies safely."]}," Policies are immutable. To change a policy's limits, create the new policy first, then delete the old one after the new policy is active. See ",{"$$mdtype":"Tag","name":"a","attributes":{"href":"/products/wallet/user-interface/policies/policies-manage#modify-a-policy"},"children":["Manage policies"]}," for the step-by-step process."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"troubleshooting","__idx":4},"children":["Troubleshooting"]},{"$$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":"Symptom"},"children":["Symptom"]},{"$$mdtype":"Tag","name":"th","attributes":{"data-label":"Cause"},"children":["Cause"]},{"$$mdtype":"Tag","name":"th","attributes":{"data-label":"Solution"},"children":["Solution"]}]}]},{"$$mdtype":"Tag","name":"tbody","attributes":{},"children":[{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Transaction rejected: \"no key limits found\""]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["No active policy exists for the asset"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Create a policy for the asset. Verify status is ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["LIMIT_ENABLED"]},"."]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Transaction rejected: \"exceeds limit\""]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Transaction amount exceeds policy limit"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Reduce amount, or delete and recreate policy with higher limit. For rolling limits, wait for older transactions to roll off."]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Transaction rejected: \"policy check failed\""]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Multiple policies apply and one rejects"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["List all policies for the wallet and check which ones match your transaction."]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":["API error ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["PAL006.023"]},": \"limit policy already exists\""]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Policy with same wallet, limit type, symbol, and matchers exists"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Delete existing policy first, or add different matchers to create a distinct policy."]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":["API error ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["PAL006.016"]},": \"action not allowed\""]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Attempting to delete a pending policy"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Wait for policy to be approved or rejected before deleting."]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":["API error ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["PAL000.002"]},": \"unauthorized request\""]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Missing API scope or vault access"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Verify credentials have ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["keylimit:read"]},", ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["keylimit:create"]},", or ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["keylimit:delete"]}," scope as needed."]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Policy shows ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["active: false"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Policy awaiting approval"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Check if status is ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["LIMIT_CREATION_APPROVAL_PENDING"]},". Ask approvers to authorize."]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Transactions passing that should be blocked"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Policy matchers don't match transaction"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Review matchers—policy only applies when ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["all"]}," matchers match."]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Policy stuck in pending"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Not enough approvers responded"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Ask additional approvers. If timeout expires, create a new policy."]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Policy rejected unexpectedly"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Approval threshold impossible to meet"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Too many approvers skipped. Create a new policy and coordinate approvers."]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["PAL006.023"]}," when creating a replacement policy"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Rejected or deleted policy still counts for uniqueness"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["A policy in ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["LIMIT_REJECTED"]}," or ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["LIMIT_DELETED"]}," status still occupies its uniqueness slot. Add a different matcher to create a distinct policy."]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Transactions blocked after deleting a counterparty or address"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Policy has orphaned matcher reference"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["The policy still exists but its matcher references a deleted entity, so no transaction can match it. Delete the orphaned policy and create a new one. See ",{"$$mdtype":"Tag","name":"a","attributes":{"href":"/products/wallet/user-interface/policies/policies-reference#combining-matchers"},"children":["Policy reference"]}," for details."]}]}]}]}]},{"$$mdtype":"Tag","name":"Admonition","attributes":{"type":"info","name":"Known display issue"},"children":[{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Rolling limit rejection messages may display \"0-hour\" regardless of the actual duration configured. The enforcement uses the correct duration."]}]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"related-documentation","__idx":5},"children":["Related documentation"]},{"$$mdtype":"Tag","name":"ul","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"a","attributes":{"href":"/products/wallet/user-interface/policies/policies-concepts"},"children":["Policy concepts"]}," – Core concepts and lifecycle"]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"a","attributes":{"href":"/products/wallet/user-interface/policies/policies-reference"},"children":["Policy reference"]}," – Complete API reference"]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"a","attributes":{"href":"/products/wallet/user-interface/policies/policies-manage"},"children":["Manage policies"]}," – Step-by-step guide"]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"a","attributes":{"href":"/products/wallet/user-interface/security-controls/approvals"},"children":["Approvals"]}," – Configure approval groups"]}]}]},"headings":[{"value":"Policy best practices","id":"policy-best-practices","depth":1},{"value":"Planning your policy strategy","id":"planning-your-policy-strategy","depth":2},{"value":"Configuration examples","id":"configuration-examples","depth":2},{"value":"Ongoing operations","id":"ongoing-operations","depth":2},{"value":"Troubleshooting","id":"troubleshooting","depth":2},{"value":"Related documentation","id":"related-documentation","depth":2}],"frontmatter":{"title":"Policy best practices","seo":{"title":"Policy best practices"}},"lastModified":"2026-04-07T14:25:06.000Z","pagePropGetterError":{"message":"","name":""}},"slug":"/products/wallet/user-interface/policies/policies-best-practices","userData":{"isAuthenticated":false,"teams":["anonymous"]},"isPublic":true}