# Manage policy rules via the API

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](/pt-br/products/wallet/governance-and-controls/policies#how-the-rule-kinds-work-together). To manage rules in the console instead, see [Manage policies via the console](/pt-br/products/wallet/governance-and-controls/policies/manage-policies).

## Policy actions

| Action | Console role | Console procedure | API procedure |
|  --- | --- | --- | --- |
| Create a limit, approval, or Travel Rule rule | Owner, Administrator | [Create a policy](/pt-br/products/wallet/governance-and-controls/policies/manage-policies#create-a-policy-via-the-console) | [Create a policy rule](#create-a-policy-rule-via-the-api) |
| Check rule status | All except Viewer | [View policy status](/pt-br/products/wallet/governance-and-controls/policies/manage-policies#view-policy-status-via-the-console) | [List a wallet's policy rules](#list-a-wallets-policy-rules-via-the-api) |
| Get a policy rule | All except Viewer | Open the wallet's **Policy** tab. | [Get a policy rule](#get-a-policy-rule-via-the-api) |
| Change a rule | Owner, Administrator | [Modify a policy](/pt-br/products/wallet/governance-and-controls/policies/manage-policies#modify-a-policy-via-the-console) | [Change a policy rule](#change-a-policy-rule-via-the-api) |
| Delete a rule | Owner, Administrator | [Delete a policy](/pt-br/products/wallet/governance-and-controls/policies/manage-policies#delete-a-policy-via-the-console) | [Delete a policy rule](#delete-a-policy-rule-via-the-api) |
| Manage wallet limits | - | - | [Use the wallet limits endpoints](#use-the-wallet-limits-endpoints-via-the-api) |


**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](/pt-br/products/wallet/organization-and-access/users-and-roles#policies). The API procedures need an API credential of type `CONTROLS`. See [Prerequisites](#prerequisites).

## Prerequisites

- **An API credential of type `CONTROLS`.** Grant it the `policy:create`, `policy:read`, and `policy:delete` permissions at organization, vault, or wallet scope. The older wallet limits endpoints use the separate `keylimit:create`, `keylimit:read`, and `keylimit:delete` permissions. Without the `policy:*` permissions, the policy rules endpoints return HTTP 403 with `PAL000.002`, even when the wallet limits endpoints work. See [API credentials overview](/pt-br/products/wallet/organization-and-access/api-credentials) and [Manage API credentials](/pt-br/products/wallet/organization-and-access/api-credentials/manage-api-credentials).
- **An access token.** Exchange your credential's `clientId` and `clientSecret` for a token. See [Getting started with the API](/pt-br/products/wallet/get-started/use-the-api#step-2-authenticate).
- **A vault and wallet.** Policy rules belong to one wallet. See [Create your first vault](/pt-br/products/wallet/get-started/console/create-your-first-vault) and [Create your first wallet](/pt-br/products/wallet/get-started/console/create-your-first-wallet).
- **The asset's ID.** Policy rules identify the asset by its registry ID, for example `3::XRP`. Read `asset.id` from the wallet's balances (`GET /v2/vaults/{vaultId}/wallets/{walletId}/balances`, which needs a `WALLETS` credential), 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](/pt-br/products/wallet/api-and-developer-experience/api-conventions).

Policy rule approvals
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.

## Create a policy rule via the API

**API reference:** [Create a policy rule](/pt-br/products/wallet/api-docs/palisade-api/palisade-api/policies/policyservice_createpolicyrule)

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

```bash
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](/pt-br/products/wallet/governance-and-controls/approvals/step-up-approvals#create-a-step-up-approval-rule-via-the-api) and [Create a Travel Rule policy rule via the API](/pt-br/products/wallet/compliance/travel-rule/set-up-travel-rule#create-a-travel-rule-policy-rule-via-the-api).

### Request body

| 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](/pt-br/products/wallet/governance-and-controls/policies/policy-reference#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`. |


### Amount condition

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


CHAIN_ID matchers reject natively integrated chains
A `CHAIN_ID` matcher authorizes [cross-chain raw signing](/pt-br/products/wallet/transactions/raw-signing#cross-chain-raw-signing-evm) 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.

### Response

The response is the new rule. With a **Policy rules** approval group, it has status `RULE_CREATION_APPROVAL_PENDING` and `active: false`:

```json
{
  "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](/pt-br/products/wallet/governance-and-controls/policies/policy-reference#rule-statuses). For error codes, see [Policy reference](/pt-br/products/wallet/governance-and-controls/policies/policy-reference#error-codes).

## List a wallet's policy rules via the API

**API reference:** [List policy rules](/pt-br/products/wallet/api-docs/palisade-api/palisade-api/policies/policyservice_listpolicyrules), [List policy rules with a request body](/pt-br/products/wallet/api-docs/palisade-api/palisade-api/policies/policyservice_listpolicyrules2)

**Required permission:** `policy:read`

```bash
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) and `pagination.pageToken`. Pass `filter.nextPageToken` from the response as the next `pagination.pageToken`. The top-level `pageSize` and `pageToken` also work on their own, but a request that sends both returns HTTP 400, so use the `pagination.*` form.
- **Filters:** `filter.kind`, `filter.trigger`, and `filter.status` take one enum value, for example `filter.status=RULE_ENABLED`. `filter.id`, `filter.vaultId`, `filter.walletId`, `filter.assetId`, `filter.active`, `filter.createdAt`, and `filter.updatedAt` take an operator, for example `filter.assetId.eq=3::XRP` or `filter.createdAt.after=2026-09-01T00:00:00Z`. `eq` is case-sensitive.
- **Search:** `search` matches the rule ID, vault ID, wallet ID, and asset ID, without regard to case.
- **Sorting:** `pagination.orderBy` takes `kind`, `trigger`, `status`, `active`, `created_at`, or `updated_at`, and `pagination.order` takes `SORT_ORDER_ASC` or `SORT_ORDER_DESC`. Field names are in snake case: `createdAt` returns 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:

```json
{
  "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"
          }
        }
      }
    }
  ]
}
```

## Get a policy rule via the API

**API reference:** [Get a policy rule](/pt-br/products/wallet/api-docs/palisade-api/palisade-api/policies/policyservice_getpolicyrule)

**Required permission:** `policy:read`

```bash
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.

## Change a policy rule via the API

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.

## Delete a policy rule via the API

**API reference:** [Delete a policy rule](/pt-br/products/wallet/api-docs/palisade-api/palisade-api/policies/policyservice_deletepolicyrule)

**Required permission:** `policy:delete`

```bash
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](/pt-br/products/wallet/governance-and-controls/policies/policy-reference#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`.

## Use the wallet limits endpoints via the API

**API reference:** [Create a wallet limit policy](/pt-br/products/wallet/api-docs/palisade-api/palisade-api/policies/policyservice_createwalletlimit), [List wallet limit policies](/pt-br/products/wallet/api-docs/palisade-api/palisade-api/policies/policyservice_listwalletlimits), [List wallet limit policies with a request body](/pt-br/products/wallet/api-docs/palisade-api/palisade-api/policies/policyservice_listwalletlimits2), [Get a wallet limit policy](/pt-br/products/wallet/api-docs/palisade-api/palisade-api/policies/policyservice_getwalletlimit), [Delete a wallet limit policy](/pt-br/products/wallet/api-docs/palisade-api/palisade-api/policies/policyservice_deletewalletlimit), [List organization wallet limits](/pt-br/products/wallet/api-docs/palisade-api/palisade-api/policies/policyservice_listglobalwalletlimits), [List organization wallet limits with a request body](/pt-br/products/wallet/api-docs/palisade-api/palisade-api/policies/policyservice_listglobalwalletlimits2)

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.

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

```json
{
  "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.

```bash
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](/pt-br/products/wallet/governance-and-controls/policies/policy-reference#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](/pt-br/products/wallet/governance-and-controls/policies/policy-reference#error-codes).

## Related documentation

- [Policies](/pt-br/products/wallet/governance-and-controls/policies)
- [Manage policies via the console](/pt-br/products/wallet/governance-and-controls/policies/manage-policies)
- [Policy reference](/pt-br/products/wallet/governance-and-controls/policies/policy-reference)
- [Ripple Custody (Wallet) API reference](/pt-br/products/wallet/api-docs/palisade-api/palisade-api)