Skip to content

This page is a reference for policy rule limit types, matchers, statuses, permissions, and error codes. For the API operations, see Manage policy rules via the API. For the full request and response schemas, see the Policies operations in the Ripple Custody (Wallet) API reference.

Quick reference

Limit types

Limit typePolicy rules aggregationWallet limits limitTypeDuration requiredDescription
Per transactionSINGLE_TXPER_TXNoMaximum per single transaction
RollingROLLINGROLLING_DURATIONYesMaximum over a sliding time window
Max total valueTOTALCONSTANTNoLifetime cap on total withdrawals

LIMIT rules support all three. APPROVAL and TRAVEL_RULE rules support SINGLE_TX only. For how the wallet limits endpoints show the other fields, see Use the wallet limits endpoints.

Matcher types

MatcherAPI valueFilters by
Transaction typeTRANSACTION_TYPEType of blockchain transaction
UserUSERUser who initiated the transaction
API credentialAPI_CREDENTIALAPI credential that submitted the request
AddressADDRESS_IDDestination address (from address book)
CounterpartyCOUNTERPARTY_IDCounterparty receiving funds
All counterpartiesALL_COUNTERPARTIESAny destination that resolves to a counterparty
WalletWALLET_IDDestination wallet in your organization
Sign forSIGN_FORAddress being signed for (multi-sig)
Chain IDCHAIN_IDEVM chain ID for cross-chain raw signing

Duration values

DurationValueNotes
1 hour"3600s"60 × 60 seconds
4 hours"14400s"60 × 60 × 4 seconds
24 hours"86400s"60 × 60 × 24 seconds
7 days"604800s"60 × 60 × 24 × 7 seconds
30 days"2592000s"60 × 60 × 24 × 30 seconds

Limit types

A LIMIT rule sets its limit type in definition.limit.amount.aggregation. Every amount condition uses operator GT: the rule limits transactions of more than threshold.

Per transaction

Limits the maximum amount for any single transaction.

FieldValue
aggregation"SINGLE_TX" (wallet limits: limitType "PER_TX")
durationNot used. The wallet limits endpoints return "0s".

Example: Limit single ETH transactions to 10 ETH maximum.

{
  "kind": "LIMIT",
  "limit": {
    "amount": { "threshold": "10", "operator": "GT", "aggregation": "SINGLE_TX" }
  }
}

Rolling

Limits the cumulative amount over a sliding time window.

FieldValue
aggregation"ROLLING" (wallet limits: limitType "ROLLING_DURATION")
durationRequired. Window size in seconds, less than one year. The policy rules API rejects a ROLLING rule without it with PAL006.040. The wallet limits endpoints don't enforce it: a ROLLING_DURATION limit sent without it is created with "0s".

Common duration values:

DurationValue
1 hour"3600s"
24 hours"86400s"
7 days"604800s"
30 days"2592000s"

Example: Limit ETH withdrawals to 100 ETH per 24 hours.

{
  "kind": "LIMIT",
  "limit": {
    "amount": { "threshold": "100", "operator": "GT", "aggregation": "ROLLING", "duration": "86400s" }
  }
}

Max total value

Sets a lifetime cap on total withdrawals. Once reached, no further withdrawals are permitted.

FieldValue
aggregation"TOTAL" (wallet limits: limitType "CONSTANT")
durationNot used. The wallet limits endpoints return "0s".

Example: Limit total ETH withdrawals to 1,000 ETH ever.

{
  "kind": "LIMIT",
  "limit": {
    "amount": { "threshold": "1000", "operator": "GT", "aggregation": "TOTAL" }
  }
}

Matchers

Matchers filter which transactions a policy rule applies to. Add matchers to create rules that target specific scenarios.

  • Send each matcher as { "type": "...", "values": ["..."] }. Don't use the older single value string, which is deprecated. Responses echo each matcher with both values and value.
  • Include each matcher type at most once, with up to 100 values. A transaction must match every matcher, and any one value in each matcher.
  • The API rejects the deprecated COUNTERPARTY type. Use COUNTERPARTY_ID instead.
  • ALL_COUNTERPARTIES takes no values. It matches only destinations that resolve to a counterparty. To apply a rule to any destination, leave out destination matchers.
  • APPROVAL rules don't support matchers.

Matcher types

TypeDescriptionValue format
TRANSACTION_TYPEFilter by transaction typeTransaction type string
USERFilter by initiating userUser UUID
API_CREDENTIALFilter by API credential usedCredential UUID
ADDRESS_IDFilter by destination addressAddress book entry UUID
COUNTERPARTY_IDFilter by counterpartyCounterparty UUID
ALL_COUNTERPARTIESFilter by destinations that resolve to any counterpartyNone (send an empty values array)
WALLET_IDFilter by destination wallet in your organizationWallet UUID
SIGN_FORFilter by address being signed forBlockchain address
CHAIN_IDFilter by EVM chain ID (cross-chain raw signing)Positive integer

TRANSACTION_TYPE

Applies the rule only to transactions of a specific type.

Values by blockchain:

BlockchainTransaction types
Ethereum/EVMTransfer
XRP LedgerPayment, OfferCreate, OfferCancel, TrustSet, EscrowCreate, EscrowFinish, AMMCreate, AMMDeposit, AMMWithdraw
BitcoinTransfer
XRP Ledger transaction types

Transaction type values are case-sensitive and use PascalCase (for example, OfferCreate, not OFFER_CREATE). Transfers use Payment on the XRP Ledger and Transfer on other blockchains.

AMM transactions require multi-asset policies

AMMCreate and AMMDeposit transactions withdraw multiple assets from the wallet. The policy engine requires policies for each affected asset. For issued assets (such as RLUSD or USD), the policy must include the issuer. See the AMM APIs changelog for details.

{
  "trigger": "OUTBOUND_INITIATED",
  "assetId": "3::XRP",
  "matchers": [
    { "type": "TRANSACTION_TYPE", "values": ["Payment"] }
  ],
  "definition": {
    "kind": "LIMIT",
    "limit": {
      "amount": { "threshold": "1000", "operator": "GT", "aggregation": "SINGLE_TX" }
    }
  }
}

USER

Applies the rule only when a specific user initiates the transaction.

{
  "matchers": [
    {
      "type": "USER",
      "values": ["8617b5b5-dfcf-4ffc-9f46-f7cec35a88bd"]
    }
  ]
}

API_CREDENTIAL

Applies the rule only when a specific API credential submits the transaction.

{
  "matchers": [
    {
      "type": "API_CREDENTIAL",
      "values": ["cred-uuid-here"]
    }
  ]
}

ADDRESS_ID

Applies the rule only when sending to a specific address from your address book.

{
  "matchers": [
    {
      "type": "ADDRESS_ID",
      "values": ["019d1b00-c1f5-77a0-bda7-0a3d09c668b9"]
    }
  ]
}

COUNTERPARTY_ID

Applies the rule only when sending to a specific counterparty.

{
  "matchers": [
    {
      "type": "COUNTERPARTY_ID",
      "values": ["019d1b00-c1f5-77a0-bda7-0a3d09c668b9"]
    }
  ]
}

ALL_COUNTERPARTIES

Applies the rule to any transaction whose destination resolves to a counterparty. Use it to set one limit for all counterparty payments without listing each counterparty.

ALL_COUNTERPARTIES takes no values, and matches only destinations that resolve to a counterparty. To apply a rule to any destination, don't add a destination matcher.

{
  "matchers": [
    {
      "type": "ALL_COUNTERPARTIES",
      "values": []
    }
  ]
}

SIGN_FOR

Applies the rule only when signing for a specific address. Use this matcher for multi-signature scenarios where your wallet signs on behalf of another address.

{
  "matchers": [
    {
      "type": "SIGN_FOR",
      "values": ["rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh"]
    }
  ]
}

CHAIN_ID

Authorizes cross-chain raw signing for a specific EVM chain ID that Ripple Custody (Wallet) doesn't natively integrate.

The rule applies only when the raw transaction's chainId matches the matcher value. Ripple Custody (Wallet) excludes rules without a CHAIN_ID matcher from cross-chain raw signing — you must add an explicit CHAIN_ID matcher to authorize the sign. Add one CHAIN_ID matcher per allowed chain.

Value format: positive integer. A value that isn't an integer returns PAL006.028. Ripple Custody (Wallet) rejects natively-integrated chain IDs (for example, Ethereum mainnet 1, Arbitrum 42161) — use the standard raw signing flow for those chains. This check doesn't apply to non-EVM wallets: an XRP Ledger wallet accepts a CHAIN_ID of 1.

Example: Limit raw signing on the Optimism chain (chain ID 10) to 1 transaction per hour.

{
  "trigger": "OUTBOUND_INITIATED",
  "assetId": "2::ETH",
  "matchers": [
    { "type": "CHAIN_ID", "values": ["10"] }
  ],
  "definition": {
    "kind": "LIMIT",
    "limit": {
      "amount": { "threshold": "1", "operator": "GT", "aggregation": "ROLLING", "duration": "3600s" }
    }
  }
}
EVM wallets only

CHAIN_ID matchers apply only to EVM wallets and only to raw-signed transactions. The target chain ID must not be one Ripple Custody (Wallet) already integrates — those go through the standard raw signing flow instead.

Orphaned matcher references

If you delete a counterparty, address, or user that is referenced by a policy's matcher, the policy continues to exist with the now-invalid reference. No transaction will match the deleted entity, so the policy becomes effectively unmatchable. If it is the only policy for that asset, this silently blocks all matching transactions. Always check for policies referencing an entity before deleting it.

Combining matchers

You can add multiple matchers to a single rule. The transaction must match all matchers for the rule to apply.

Example: Limit a specific user to 50 ETH per transaction when sending to a specific counterparty.

{
  "trigger": "OUTBOUND_INITIATED",
  "assetId": "2::ETH",
  "matchers": [
    { "type": "USER", "values": ["user-uuid-here"] },
    { "type": "COUNTERPARTY_ID", "values": ["counterparty-uuid-here"] }
  ],
  "definition": {
    "kind": "LIMIT",
    "limit": {
      "amount": { "threshold": "50", "operator": "GT", "aggregation": "SINGLE_TX" }
    }
  }
}

ERC-20 tokens

Policy rules identify a token by its registry ID in assetId, so the rule covers that token contract only. To find a token's registry ID, list assets from the registry:

GET /v2/registry/assets?filter.blockchain.eq=<blockchain>

The wallet limits endpoints identify the token by symbol and contract instead. Include the contract field with the token's contract address:

{
  "limitType": "PER_TX",
  "symbol": "USDC",
  "limitQty": "10000",
  "contract": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48"
}
Contract address required

If you omit the contract field for an ERC-20 token in the wallet limits endpoints, the API may accept the request but the policy will not match any transactions. Always include the contract address for ERC-20 policies.

Rule statuses

StatusEnforcing (active)Can deleteDescription
RULE_CREATION_APPROVAL_PENDINGNoNoThe rule is waiting for approvers to approve it.
RULE_ENABLEDYesYesThe rule is active and enforcing.
RULE_REJECTEDNoYesApprovers rejected the rule. It can't be enabled again. A rejected rule still blocks you from creating an equivalent rule until you delete it. Deleting it moves it straight to RULE_DELETED, with no approval.
RULE_DELETION_APPROVAL_PENDINGYesNoThe rule is waiting for approvers to approve its deletion, and keeps enforcing until they do. If approvers reject the deletion, the rule returns to RULE_ENABLED.
RULE_DELETEDNoNoApprovers approved the deletion. This status is final.
RULE_ERRORNoYesRipple Custody (Wallet) couldn't process the rule. Delete the rule and create it again, or contact Ripple Support.

A rule also passes through the short-lived statuses RULE_CREATED, RULE_CREATION_APPROVAL_COMPLETE, and RULE_DELETION_APPROVAL_COMPLETE. The active field is true only for RULE_ENABLED and RULE_DELETION_APPROVAL_PENDING.

The wallet limits endpoints report the same statuses with a LIMIT_ prefix. See Use the wallet limits endpoints.

Permissions

The policy rules API uses the policy permission family, which is separate from keylimit. A keylimit permission doesn't give access to the policy rules API, and a policy permission doesn't give access to the /policy-rules/limits endpoints.

PermissionLets the credential
policy:createCreate rules
policy:readList and get rules
policy:deleteDelete rules

The wallet limits endpoints use keylimit:create, keylimit:read, and keylimit:delete in the same way.

When you create an API credential, you can grant each permission at organization, vault, or wallet scope. In the console, owners and administrators can create and delete rules. Owners, administrators, proposers, approvers, and auditors can view rules. Viewers can't.

Error codes

CodeMessageCause
PAL000.002unauthorized requestThe credential is missing the required permission, or you don't have access to the vault. The policy rules endpoints return HTTP 403 with this code without the policy:* permissions, even when the wallet limits endpoints work.
PAL000.020invalid requestThe request fails schema validation, for example a missing trigger or operator, an unknown kind or matcher type, a negative threshold, a duration of one year or more, values on an ALL_COUNTERPARTIES matcher, the deprecated COUNTERPARTY matcher type, or a definition object that doesn't match kind.
PAL006.016the action you are attempting to perform is not allowedYou tried to delete a rule that is waiting for creation or deletion approval. The wallet limits endpoints also return this code for a wallet limit ID that doesn't exist on the wallet.
PAL006.023limit policy already existsWallet limits endpoints only. HTTP 409. The wallet already has a limit, including a rejected one, with the same limit type, symbol, and matchers. The amount and duration don't count, so a limit that differs only in limitQty or duration is a duplicate.
PAL006.028invalid matcher valueA matcher value has the wrong format, for example a COUNTERPARTY_ID value that isn't a UUID or a CHAIN_ID value that isn't an integer.
PAL006.038this policy rule type cannot be created yetThis rule kind isn't available for your organization.
PAL006.039an equivalent policy rule already existsHTTP 409. The wallet already has a rule, not yet deleted, with the same kind, trigger, asset, aggregation, and matchers. The duration never counts. For LIMIT and TRAVEL_RULE rules, the threshold doesn't count either, so two LIMIT rules that differ only in amount or window are duplicates. For APPROVAL rules, the threshold does count, so a different threshold makes the rule distinct. Rules created through the wallet limits endpoints count too.
PAL006.040(varies)The rule is invalid. The message names the problem, for example amount operator GTE is not yet supported, ROLLING aggregation requires a positive duration, an amount condition requires an explicit asset, limit rules are outbound only (OUTBOUND_INITIATED), or an asset on a different blockchain from the wallet.
PAL006.041policy rule not foundHTTP 404. The rule doesn't exist on this wallet.
PAL006.046travel-rule policy rules require a configured travel-rule connectionYou tried to create a TRAVEL_RULE rule before you connected Notabene.
PAL006.048approval rules cannot be created on a wallet with enabled compliance integrationsYou tried to create an APPROVAL rule on a wallet that has compliance screening integrations turned on.

For transaction rejection codes, see Transaction status changes.