Skip to content

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 typePurpose
Per transactionPrevents large single transactions
Rolling durationPrevents rapid accumulation of smaller transactions
Max total valueProvides an absolute backstop

For example, a hot wallet holding ETH might use three policies together:

{"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 policy:create or keylimit:create API permissions can instantly enable policies. See 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 transaction limit configured for <asset>.

Configuration examples

The following examples show common policy configurations for different wallet types.

Hot wallet – Process many small transactions automatically with tight limits:

{"limitType": "PER_TX", "symbol": "ETH", "limitQty": "1"}
{"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:

{"limitType": "PER_TX", "symbol": "ETH", "limitQty": "1000", "matchers": [{"type": "COUNTERPARTY_ID", "values": ["trusted-uuid"]}]}
{"limitType": "PER_TX", "symbol": "ETH", "limitQty": "10"}

Role-based limits – Use USER matchers to set different limits for different team members:

{"limitType": "PER_TX", "symbol": "ETH", "limitQty": "5", "matchers": [{"type": "USER", "values": ["ops-user-uuid"]}]}
{"limitType": "PER_TX", "symbol": "ETH", "limitQty": "100", "matchers": [{"type": "USER", "values": ["finance-user-uuid"]}]}

API credential isolation – Use API_CREDENTIAL matchers to limit each integration's authority:

{"limitType": "PER_TX", "symbol": "ETH", "limitQty": "1", "matchers": [{"type": "API_CREDENTIAL", "values": ["trading-bot-uuid"]}]}
{"limitType": "ROLLING_DURATION", "symbol": "ETH", "limitQty": "100", "duration": "86400s", "matchers": [{"type": "API_CREDENTIAL", "values": ["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. When a policy with matchers matches a transaction, it replaces the policies without matchers for that transaction, including rolling and max-total-value policies. Add matching rolling or lifetime policies with the same matchers if you want those caps to keep applying:

{"limitType": "PER_TX", "symbol": "ETH", "limitQty": "10"}
{"limitType": "PER_TX", "symbol": "ETH", "limitQty": "100", "matchers": [{"type": "COUNTERPARTY_ID", "values": ["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 via the console for the step-by-step process.

Troubleshooting

SymptomCauseSolution
Transaction rejected: No transaction limit configured for <asset> or Transaction limits for <asset> exist but are not active (PAL006.005)No active policy exists for the assetCreate a limit rule for the asset. Verify status is RULE_ENABLED.
Transaction rejected: "exceeds limit"Transaction amount exceeds policy limitReduce 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 rejectsList all policies for the wallet and check which ones match your transaction.
API error PAL006.039: "an equivalent policy rule already exists" (or PAL006.023: "limit policy already exists" from the wallet limits endpoints)Rule with same wallet, kind, trigger, asset, limit type, and matchers existsDelete existing policy first, or add different matchers to create a distinct policy.
API error PAL006.016: "action not allowed"Attempting to delete a pending policyWait for policy to be approved or rejected before deleting.
API error PAL000.002: "unauthorized request"Missing API scope or vault accessVerify credentials have the policy:* permissions (policy rules API) or keylimit:* permissions (wallet limits endpoints) as needed.
Policy shows active: falsePolicy awaiting approvalCheck if status is RULE_CREATION_APPROVAL_PENDING. Ask approvers to authorize.
Transactions passing that should be blockedPolicy matchers don't match transactionReview matchers—policy only applies when all matchers match.
Policy stuck in pendingNot enough approvers respondedAsk additional approvers. If timeout expires, create a new policy.
Policy rejected unexpectedlyApproval threshold impossible to meetToo many approvers skipped. Create a new policy and coordinate approvers.
PAL006.039 or PAL006.023 when creating a replacement policyRejected policy still counts for uniquenessA policy in RULE_REJECTED status still occupies its uniqueness slot. Deleted policies don't. Delete the rejected policy, then create the replacement.
Transactions blocked after deleting a counterparty or addressPolicy has orphaned matcher referenceThe 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 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.