# Policy best practices This page provides recommended patterns for planning, configuring, and maintaining transaction policies. ## Planning your policy strategy **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. **Layer your limits.** Combine multiple limit types for defense in depth: | Limit type | Purpose | | --- | --- | | Per transaction | Prevents large single transactions | | Rolling duration | Prevents rapid accumulation of smaller transactions | | Max total value | Provides an absolute backstop | For example, a hot wallet holding ETH might use three policies together: ```json {"limitType": "PER_TX", "symbol": "ETH", "limitQty": "10"} {"limitType": "ROLLING_DURATION", "symbol": "ETH", "limitQty": "50", "duration": "86400s"} {"limitType": "CONSTANT", "symbol": "ETH", "limitQty": "500"} ``` 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. **Enable approval groups.** Without approval groups, anyone with `keylimit:create` API permissions can instantly enable policies. See [Approvals](/products/wallet/user-interface/security-controls/approvals) to add human oversight. **Avoid common mistakes:** - Don't use zero or negative limits, they block all transactions or behave unpredictably. - Don't rely on a single high-limit policy without matchers—it provides minimal protection. - Don't rely solely on per-transaction limits—combine with rolling limits to catch accumulation attacks. - Don't leave assets without policies—transactions will fail with "no key limits found". ## Configuration examples The following examples show common policy configurations for different wallet types. **Hot wallet** – Process many small transactions automatically with tight limits: ```json {"limitType": "PER_TX", "symbol": "ETH", "limitQty": "1"} {"limitType": "ROLLING_DURATION", "symbol": "ETH", "limitQty": "10", "duration": "3600s"} {"limitType": "ROLLING_DURATION", "symbol": "ETH", "limitQty": "50", "duration": "86400s"} ``` Multiple rolling policies 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. **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. **Treasury wallet** – Handle large transfers to trusted counterparties while restricting other destinations: ```json {"limitType": "PER_TX", "symbol": "ETH", "limitQty": "1000", "matchers": [{"type": "COUNTERPARTY_ID", "value": "trusted-uuid"}]} {"limitType": "PER_TX", "symbol": "ETH", "limitQty": "10"} ``` **Role-based limits** – Use `USER` matchers to set different limits for different team members: ```json {"limitType": "PER_TX", "symbol": "ETH", "limitQty": "5", "matchers": [{"type": "USER", "value": "ops-user-uuid"}]} {"limitType": "PER_TX", "symbol": "ETH", "limitQty": "100", "matchers": [{"type": "USER", "value": "finance-user-uuid"}]} ``` **API credential isolation** – Use `API_CREDENTIAL` matchers to limit each integration's authority: ```json {"limitType": "PER_TX", "symbol": "ETH", "limitQty": "1", "matchers": [{"type": "API_CREDENTIAL", "value": "trading-bot-uuid"}]} {"limitType": "ROLLING_DURATION", "symbol": "ETH", "limitQty": "100", "duration": "86400s", "matchers": [{"type": "API_CREDENTIAL", "value": "settlement-uuid"}]} ``` ## Ongoing operations **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. **Monitor transaction rejections.** Frequent rejections may indicate limits set too low, attempted unauthorized access, or misconfigured policies. Use the transaction list to filter by `REJECTED` status and review the rejection reasons. **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. **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: ```json {"limitType": "PER_TX", "symbol": "ETH", "limitQty": "10"} {"limitType": "PER_TX", "symbol": "ETH", "limitQty": "100", "matchers": [{"type": "COUNTERPARTY_ID", "value": "trusted-uuid"}]} ``` **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 [Manage policies](/products/wallet/user-interface/policies/policies-manage#modify-a-policy) for the step-by-step process. ## Troubleshooting | Symptom | Cause | Solution | | --- | --- | --- | | Transaction rejected: "no key limits found" | No active policy exists for the asset | Create a policy for the asset. Verify status is `LIMIT_ENABLED`. | | Transaction rejected: "exceeds limit" | Transaction amount exceeds policy limit | Reduce amount, or delete and recreate policy with higher limit. For rolling limits, wait for older transactions to roll off. | | Transaction rejected: "policy check failed" | Multiple policies apply and one rejects | List all policies for the wallet and check which ones match your transaction. | | API error `PAL006.023`: "limit policy already exists" | Policy with same wallet, limit type, symbol, and matchers exists | Delete existing policy first, or add different matchers to create a distinct policy. | | API error `PAL006.016`: "action not allowed" | Attempting to delete a pending policy | Wait for policy to be approved or rejected before deleting. | | API error `PAL000.002`: "unauthorized request" | Missing API scope or vault access | Verify credentials have `keylimit:read`, `keylimit:create`, or `keylimit:delete` scope as needed. | | Policy shows `active: false` | Policy awaiting approval | Check if status is `LIMIT_CREATION_APPROVAL_PENDING`. Ask approvers to authorize. | | Transactions passing that should be blocked | Policy matchers don't match transaction | Review matchers—policy only applies when **all** matchers match. | | Policy stuck in pending | Not enough approvers responded | Ask additional approvers. If timeout expires, create a new policy. | | Policy rejected unexpectedly | Approval threshold impossible to meet | Too many approvers skipped. Create a new policy and coordinate approvers. | | `PAL006.023` when creating a replacement policy | Rejected or deleted policy still counts for uniqueness | A policy in `LIMIT_REJECTED` or `LIMIT_DELETED` status still occupies its uniqueness slot. Add a different matcher to create a distinct policy. | | Transactions blocked after deleting a counterparty or address | Policy has orphaned matcher reference | 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 [Policy reference](/products/wallet/user-interface/policies/policies-reference#combining-matchers) for details. | Known display issue Rolling limit rejection messages may display "0-hour" regardless of the actual duration configured. The enforcement uses the correct duration. ## Related documentation - [Policy concepts](/products/wallet/user-interface/policies/policies-concepts) – Core concepts and lifecycle - [Policy reference](/products/wallet/user-interface/policies/policies-reference) – Complete API reference - [Manage policies](/products/wallet/user-interface/policies/policies-manage) – Step-by-step guide - [Approvals](/products/wallet/user-interface/security-controls/approvals) – Configure approval groups