This guide shows you how to create, list, get, and delete a wallet's policy rules through the Ripple Custody (Wallet) API. A policy rule has a kind: LIMIT rules let a wallet send up to an amount, APPROVAL rules hold large transfers for named approvers, and TRAVEL_RULE rules require a Travel Rule data exchange. A wallet can't send an asset until a LIMIT rule applies to it.
For how the rule kinds work together, see Policies. To manage rules in the console instead, see Manage policies via the console.
| Action | Console role | Console procedure | API procedure |
|---|---|---|---|
| Create a limit, approval, or Travel Rule rule | Owner, Administrator | Create a policy | Create a policy rule |
| Check rule status | All except Viewer | View policy status | List a wallet's policy rules |
| Get a policy rule | All except Viewer | Open the wallet's Policy tab. | Get a policy rule |
| Change a rule | Owner, Administrator | Modify a policy | Change a policy rule |
| Delete a rule | Owner, Administrator | Delete a policy | Delete a policy rule |
| Manage wallet limits | - | - | Use the wallet limits endpoints |
Permissions: In the console, owners and administrators can create and delete policy rules. Owners, administrators, proposers, and approvers can approve them, and every role except viewer can view them. See User roles and permissions. The API procedures need an API credential of type CONTROLS. See Prerequisites.
- An API credential of type
CONTROLS. Grant it thepolicy:create,policy:read, andpolicy:deletepermissions at organization, vault, or wallet scope. The older wallet limits endpoints use the separatekeylimit:create,keylimit:read, andkeylimit:deletepermissions. Without thepolicy:*permissions, the policy rules endpoints return HTTP 403 withPAL000.002, even when the wallet limits endpoints work. See API credentials overview and Manage API credentials. - An access token. Exchange your credential's
clientIdandclientSecretfor a token. See Getting started with the API. - A vault and wallet. Policy rules belong to one wallet. See Create your first vault and Create your first wallet.
- The asset's ID. Policy rules identify the asset by its registry ID, for example
3::XRP. Readasset.idfrom the wallet's balances (GET /v2/vaults/{vaultId}/wallets/{walletId}/balances, which needs aWALLETScredential), or list assets from the registry. For example, to find a native asset, filter by blockchain and standard:GET /v2/registry/assets?filter.blockchain.eq=<blockchain>&filter.standard.eq=NATIVE.
For the base URLs, token lifetime, pagination, filtering, and error format that every endpoint shares, see API conventions.
If your organization has a Policy rules approval group, a rule that you create or delete through the API waits for approvers before the change takes effect. A new rule has status RULE_CREATION_APPROVAL_PENDING and active: false until approvers approve it. Approvers act on the request in the console.
API reference: Create a policy rule
Required permission: policy:create
Send a POST request to /v2/vaults/{vaultId}/wallets/{walletId}/policy-rules. Set definition.kind to the rule kind, and include only the matching object: limit, approval, or travelRule.
This example creates a LIMIT rule that caps payments to any counterparty at 10,000 XRP in a rolling 24-hour window:
curl -X POST https://api.sandbox.palisade.co/v2/vaults/$VAULT_ID/wallets/$WALLET_ID/policy-rules \
-H "Authorization: Bearer $ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"trigger": "OUTBOUND_INITIATED",
"assetId": "3::XRP",
"matchers": [
{ "type": "ALL_COUNTERPARTIES", "values": [] }
],
"definition": {
"kind": "LIMIT",
"limit": {
"amount": {
"threshold": "10000",
"operator": "GT",
"aggregation": "ROLLING",
"duration": "86400s"
}
}
}
}'For APPROVAL and TRAVEL_RULE rules, use the same request with a different definition. See Create a step-up approval rule via the API and Create a Travel Rule policy rule via the API.
| Field | Type | Required | Description |
|---|---|---|---|
trigger | string | Yes | OUTBOUND_INITIATED for transactions the wallet sends, or INBOUND_DETECTED for deposits into the wallet. Only TRAVEL_RULE rules support INBOUND_DETECTED. |
assetId | string | Conditional | The asset's registry ID. LIMIT and APPROVAL rules require it. To apply a TRAVEL_RULE rule with no amount condition to all assets, leave it out. The asset's blockchain must match the wallet's blockchain. |
matchers | array | No | Filters that narrow which transactions the rule applies to. Send each matcher as { "type": "...", "values": ["..."] }. APPROVAL rules don't support matchers. See Matchers. |
definition | object | Yes | The rule kind and its settings. Set definition.kind to LIMIT, APPROVAL, or TRAVEL_RULE, and include only the matching object: limit, approval, or travelRule. |
LIMIT, APPROVAL, and TRAVEL_RULE rules use the same amount object, for example definition.limit.amount:
| Field | Type | Description |
|---|---|---|
threshold | string | Decimal amount in the asset's units, for example "10000" or "0.5". "0" is valid. A negative amount returns PAL000.020. |
operator | string | GT (greater than). The schema also lists GTE, but Ripple Custody (Wallet) doesn't support it yet and rejects it. |
aggregation | string | SINGLE_TX compares each transaction on its own. ROLLING compares the total the wallet sent during the duration window. TOTAL compares the total the wallet ever sent. |
duration | string | The window length in seconds, for example "86400s" for 24 hours. Must be less than one year. Use it only with ROLLING, which requires it. A ROLLING rule without it, or another aggregation with it, returns PAL006.040. A window of one year or more returns PAL000.020. |
| Kind | Supported aggregation values |
|---|---|
LIMIT | SINGLE_TX, ROLLING, TOTAL |
APPROVAL | SINGLE_TX |
TRAVEL_RULE | SINGLE_TX |
A CHAIN_ID matcher authorizes cross-chain raw signing for an EVM chain that Ripple Custody (Wallet) doesn't natively integrate. If the value is the chain ID of a natively integrated chain, for example Ethereum mainnet 1, Ripple Custody (Wallet) rejects the request. Ripple Custody (Wallet) doesn't check CHAIN_ID values on non-EVM wallets: it accepts a rule with a CHAIN_ID of 1 on an XRP Ledger wallet. Add CHAIN_ID matchers only to EVM wallets.
The response is the new rule. With a Policy rules approval group, it has status RULE_CREATION_APPROVAL_PENDING and active: false:
{
"id": "0199a1c2-5d3e-7a10-9b2f-4c8e1d6f0a21",
"organizationId": "a9a1de05-0b19-4fca-8597-1199353bd569",
"vaultId": "019be892-2834-7435-be18-bc13ac4c73ce",
"walletId": "019bec51-ab18-7284-907a-dd7747277116",
"createdBy": "8617b5b5-dfcf-4ffc-9f46-f7cec35a88bd",
"trigger": "OUTBOUND_INITIATED",
"assetId": "3::XRP",
"matchers": [
{ "type": "ALL_COUNTERPARTIES", "value": "", "values": [] }
],
"status": "RULE_CREATION_APPROVAL_PENDING",
"active": false,
"createdAt": "2026-09-24T12:00:00Z",
"updatedAt": "2026-09-24T12:00:00Z",
"definition": {
"kind": "LIMIT",
"limit": {
"amount": {
"threshold": "10000",
"operator": "GT",
"aggregation": "ROLLING",
"duration": "86400s"
}
}
},
"scope": "WALLET"
}The response echoes each matcher with both values and the deprecated value string, and includes duration only for ROLLING rules. scope is WALLET for rules that you create. For each status, see Rule statuses. For error codes, see Policy reference.
API reference: List policy rules, List policy rules with a request body
Required permission: policy:read
curl "https://api.sandbox.palisade.co/v2/vaults/$VAULT_ID/wallets/$WALLET_ID/policy-rules?filter.kind=LIMIT&filter.active.eq=true" \
-H "Authorization: Bearer $ACCESS_TOKEN"- Paging:
pagination.pageSize(default 50, maximum 1,000) andpagination.pageToken. Passfilter.nextPageTokenfrom the response as the nextpagination.pageToken. The top-levelpageSizeandpageTokenalso work on their own, but a request that sends both returns HTTP 400, so use thepagination.*form. - Filters:
filter.kind,filter.trigger, andfilter.statustake one enum value, for examplefilter.status=RULE_ENABLED.filter.id,filter.vaultId,filter.walletId,filter.assetId,filter.active,filter.createdAt, andfilter.updatedAttake an operator, for examplefilter.assetId.eq=3::XRPorfilter.createdAt.after=2026-09-01T00:00:00Z.eqis case-sensitive. - Search:
searchmatches the rule ID, vault ID, wallet ID, and asset ID, without regard to case. - Sorting:
pagination.orderBytakeskind,trigger,status,active,created_at, orupdated_at, andpagination.ordertakesSORT_ORDER_ASCorSORT_ORDER_DESC. Field names are in snake case:createdAtreturns HTTP 400. By default, the newest rule comes first.
To send the filters in a JSON body instead, use POST /v2/vaults/{vaultId}/wallets/{walletId}/policy-rules:list.
The response holds the rules in a policyRules array, with each rule in the same shape as the create response. This example is abridged:
{
"filter": {
"nextPageToken": "cGFnZV9zaXplPTEwJnBhZ2VfdG9rZW49MjA=",
"total": 3
},
"policyRules": [
{
"id": "0199a1c2-5d3e-7a10-9b2f-4c8e1d6f0a21",
"vaultId": "019be892-2834-7435-be18-bc13ac4c73ce",
"walletId": "019bec51-ab18-7284-907a-dd7747277116",
"trigger": "OUTBOUND_INITIATED",
"assetId": "3::XRP",
"status": "RULE_ENABLED",
"active": true,
"definition": {
"kind": "LIMIT",
"limit": {
"amount": {
"threshold": "10000",
"operator": "GT",
"aggregation": "ROLLING",
"duration": "86400s"
}
}
}
}
]
}API reference: Get a policy rule
Required permission: policy:read
curl https://api.sandbox.palisade.co/v2/vaults/$VAULT_ID/wallets/$WALLET_ID/policy-rules/$RULE_ID \
-H "Authorization: Bearer $ACCESS_TOKEN"The response is the rule, in the same shape as the create response. A rule belongs to one wallet. If the rule doesn't exist on the wallet in the path, the API returns HTTP 404 with PAL006.041, even when the rule exists on another wallet.
There is no update operation, so you can't edit a rule. To change one, create the new rule first, wait for it to become active, and then delete the old one. This order keeps the wallet protected throughout the change.
API reference: Delete a policy rule
Required permission: policy:delete
curl -X DELETE https://api.sandbox.palisade.co/v2/vaults/$VAULT_ID/wallets/$WALLET_ID/policy-rules/$RULE_ID \
-H "Authorization: Bearer $ACCESS_TOKEN"A successful request returns an empty JSON object. With a Policy rules approval group, the rule moves to RULE_DELETION_APPROVAL_PENDING and keeps enforcing until approvers approve the deletion. For every status, see Rule statuses.
You can't delete a rule that is waiting for creation or deletion approval: the API returns HTTP 400 with PAL006.016. A rule ID that doesn't exist on the wallet returns HTTP 404 with PAL006.041.
API reference: Create a wallet limit policy, List wallet limit policies, List wallet limit policies with a request body, Get a wallet limit policy, Delete a wallet limit policy, List organization wallet limits, List organization wallet limits with a request body
The wallet limits endpoints predate the policy rules API and keep working. They manage LIMIT rules only, and use the keylimit:* permissions.
Both APIs read and write the same LIMIT rules. A wallet limit appears in the policy rules API as a LIMIT rule with the same ID, and a LIMIT rule that you create with the policy rules API appears in the wallet limits endpoints. Duplicate checks span both APIs. The two APIs show the same rule in different shapes:
| Wallet limits field | Policy rules field |
|---|---|
limitType: PER_TX, ROLLING_DURATION, CONSTANT | definition.limit.amount.aggregation: SINGLE_TX, ROLLING, TOTAL |
limitQty | definition.limit.amount.threshold, with operator GT |
duration, "0s" when unused | definition.limit.amount.duration, present only for ROLLING |
asset object | assetId |
status: LIMIT_* values | status: the matching RULE_* value, for example LIMIT_CREATION_APPROVAL_PENDING and RULE_CREATION_APPROVAL_PENDING |
| No equivalent | trigger (always OUTBOUND_INITIATED) and scope |
The wallet limits endpoints are PUT, GET, and POST …/limits:list on /v2/vaults/{vaultId}/wallets/{walletId}/policy-rules/limits, GET and DELETE on /v2/vaults/{vaultId}/wallets/{walletId}/policy-rules/limits/{id}, and GET /v2/policy-rules/limits or POST /v2/policy-rules/limits:list across the organization.
To create a wallet limit, send limitQty, limitType, and symbol. Set limitType to PER_TX, CONSTANT, or ROLLING_DURATION, and set duration for ROLLING_DURATION. The window must be less than one year. Always set duration for a rolling limit: the API doesn't require it, and creates a ROLLING_DURATION limit without one with a duration of "0s". For a token, also set contract to the token contract or issuing address. blockchain defaults to the wallet's blockchain.
curl -X PUT https://api.sandbox.palisade.co/v2/vaults/$VAULT_ID/wallets/$WALLET_ID/policy-rules/limits \
-H "Authorization: Bearer $ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"limitQty": "200.00",
"limitType": "ROLLING_DURATION",
"duration": "86400s",
"symbol": "XRP",
"matchers": [
{
"type": "COUNTERPARTY_ID",
"values": ["ce4918bf-a199-4ce2-85a3-d0d296855384"]
}
]
}'The response is the new wallet limit, with the asset as an asset object (abridged in this example). Ripple Custody (Wallet) normalizes limitQty, so "200.00" comes back as "200", and echoes each matcher with both value and values:
{
"id": "f1dc7bc7-107c-40df-b254-50f2e041e575",
"createdBy": "8617b5b5-dfcf-4ffc-9f46-f7cec35a88bd",
"organizationId": "a9a1de05-0b19-4fca-8597-1199353bd569",
"walletId": "019bec51-ab18-7284-907a-dd7747277116",
"vaultId": "019be892-2834-7435-be18-bc13ac4c73ce",
"limitQty": "200",
"duration": "86400s",
"limitType": "ROLLING_DURATION",
"createdAt": "2026-09-24T12:00:00Z",
"updatedAt": "2026-09-24T12:00:00Z",
"asset": {
"id": "3::XRP",
"standard": "NATIVE",
"symbol": "XRP",
"name": "XRP",
"blockchain": "XRP_LEDGER",
"decimals": 6,
"contract": ""
},
"matchers": [
{
"type": "COUNTERPARTY_ID",
"value": "ce4918bf-a199-4ce2-85a3-d0d296855384",
"values": ["ce4918bf-a199-4ce2-85a3-d0d296855384"]
}
],
"status": "LIMIT_CREATION_APPROVAL_PENDING",
"active": false
}To audit limits across every vault and wallet, use the organization-level list. It supports pageSize, pageToken, search (wallet limit ID, vault ID, wallet ID, and asset symbol), filter.limitType, filter.status, and operator filters such as filter.walletId.eq, filter.assetSymbol.eq, and filter.blockchain.eq. The response holds the limits in a walletLimits array.
curl "https://api.sandbox.palisade.co/v2/policy-rules/limits?filter.status=LIMIT_ENABLED&filter.assetSymbol.eq=XRP" \
-H "Authorization: Bearer $ACCESS_TOKEN"For what each status means, see Rule statuses: a wallet limit in LIMIT_ENABLED is a rule in RULE_ENABLED, and so on. For error codes, including PAL006.023 from these endpoints, see Policy reference.