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 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 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:

{"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:

{"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:

{"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:

{"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:

{"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 for the step-by-step process.

Troubleshooting

SymptomCauseSolution
Transaction rejected: "no key limits found"No active policy exists for the assetCreate a policy for the asset. Verify status is LIMIT_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.023: "limit policy already exists"Policy with same wallet, limit type, symbol, 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 keylimit:read, keylimit:create, or keylimit:delete scope as needed.
Policy shows active: falsePolicy awaiting approvalCheck if status is LIMIT_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.023 when creating a replacement policyRejected or deleted policy still counts for uniquenessA 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 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.