# Policy rules (Policy Engine v2)

Policy Engine v2 adds **typed policy rules**. Each rule has a **kind** that decides what the rule does when a transaction matches it:

| Kind | What it does | Direction |
|  --- | --- | --- |
| `LIMIT` | Lets outgoing transactions proceed up to an amount, and blocks transactions that exceed it | Outbound |
| `APPROVAL` | Holds outgoing transactions of more than an amount until named approvers approve them. See [Step-up approvals](/products/wallet/user-interface/policies/step-up-approvals). | Outbound |
| `TRAVEL_RULE` | Requires a Travel Rule data exchange with the counterparty before a transfer settles. See [Travel Rule](/products/wallet/user-interface/travel-rule/travel-rule-overview). | Outbound and inbound |


Your existing wallet limits are `LIMIT` rules. They keep working, and the policy rules API returns them as `LIMIT` rules.

Preview
Typed policy rules are available only in organizations where Ripple has turned on Policy Engine v2. `APPROVAL` and `TRAVEL_RULE` rules also need Ripple to turn them on for your organization. The policy rules API on this page is in preview, and the published API reference doesn't include it yet. To turn on these features, contact your Ripple representative.

## How the rule kinds work together

- **Only `LIMIT` rules let a wallet send.** A wallet can only receive an asset until it has an applicable `LIMIT` rule for that asset. `APPROVAL` and `TRAVEL_RULE` rules never let a transaction through on their own. If no `LIMIT` rule applies, Wallet-as-a-Service rejects the transaction with `PAL006.005`.
- **Rule kinds don't override each other.** A transaction must pass its applicable `LIMIT` rules, and then meet any `APPROVAL` and `TRAVEL_RULE` requirements that apply to it.
- **A blocked transaction never reaches approvers.** If a `LIMIT` rule blocks the transaction, Wallet-as-a-Service rejects it before it creates any approval request.


### How Wallet-as-a-Service selects limit rules

For each wallet and asset, Wallet-as-a-Service sorts `LIMIT` rules into two groups:

- **Base rules** have no matchers. They apply to every outgoing transaction for the asset.
- **Scoped rules** have one or more matchers, such as `USER` or `COUNTERPARTY_ID`. A scoped rule applies only when the transaction matches every one of its matchers.


If at least one scoped rule applies to a transaction, the scoped rules **replace** the base rules for that transaction, including base rolling and lifetime limits. If no scoped rule applies, the base rules apply. The transaction must pass **every** rule in the selected group.

**Example:** A wallet has a base per-transaction limit of 10 ETH, and a scoped per-transaction limit of 50 ETH for one API credential.

- A 30 ETH transfer from that API credential passes. The scoped rule applies, so the base rule doesn't.
- A 30 ETH transfer from any other initiator fails, because the base rule applies.


## Create a policy rule in the console

1. Open the wallet and go to the **Policy** tab.
2. Select **Add a new rule**.
3. On the **Direction** step, select **Outbound** for transfers from this wallet, or **Inbound** for deposits into this wallet. Only Travel Rule rules support **Inbound**. Select **Next**.
![The Direction step of the Add policy rule form, with the Outbound and Inbound options](/assets/policy-stepper-direction.0b7cce0c07b4d56b9bcbdf2cf98f08b2813e5e964024b1277aaf48c022e2e9a1.0ba50ef8.png)
4. On the **Conditions** step, select the asset, and then select the rule under **Rule**:
  - **Limit:** Select **Max total value**, **Rolling**, or **Per transaction**, and enter the **Value limit**. For **Rolling**, select a **Duration**: 1 hour, 1 day, 1 week, 2 weeks, or 1 month.
  - **Approval:** See [Step-up approvals](/products/wallet/user-interface/policies/step-up-approvals#create-a-step-up-approval-rule-in-the-console).
  - **Travel rule:** See [Set up Travel Rule](/products/wallet/user-interface/travel-rule/travel-rule-setup#in-the-console).
![The Conditions step with a Rolling limit of 1000 XRP over 1 day](/assets/policy-stepper-limit-conditions.ba87923f8bd7aa2513f26033825cf1b8ae2f7b294ad45980d917e2255a831330.0ba50ef8.png)
5. On the **Scope** step, select the destinations the rule applies to. You can also narrow the rule by **Sign for address** and **Transaction types**. To apply the rule to every destination, leave the selection empty.
![The Scope step with destination options, including All counterparties](/assets/policy-stepper-scope.f372d6e396cd961709b105b551fd5ab56b8f063ae808a59ee0893f2c6fb7b14e.0ba50ef8.png)
6. On the **Advanced** step, leave **All** selected, or restrict the rule to one **User** or **API credential**.
7. On the **Confirm** step, review the rule, and then select **Add policy rule**.
![The Confirm step for a Rolling limit rule](/assets/policy-stepper-limit-confirm.0f60f2bbb48edc7ac939a5aad137aaf4aff1da35be3461da6009c2b187606464.0ba50ef8.png)


If your organization has a **Policy rules** approval group, the rule shows **Inactive | Approval pending** until approvers approve it. Approvers find the request under **Notifications** > **Approvals**.

![Notifications showing three pending Policy rule approval requests](/assets/policy-rule-approval-requests.e7404b86b6e2737369099a11a14b64d941d18f282b0f2bfa65713488d6e3fdf0.0ba50ef8.png)

After approval, the rule shows **Active | Enabled** on the **Policy** tab.

![The Policy tab listing Approval, Travel rule, and Limit rules, all Active and Enabled](/assets/policy-rules-list.2199fd0f166e36eb7b0e211a125cc62798b2bef6bc71498767b7cb2f950e3255.0ba50ef8.png)

## Create a policy rule with the API

```
POST /v2/vaults/{vaultId}/wallets/{walletId}/policy-rules
```

Your API credential needs the `policy:create` permission at organization, vault, or wallet scope. See [Permissions](#permissions).

### 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. See [Matchers](#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`. |


Registry IDs look like `3::XRP`. To find an asset's registry ID, 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
```

### Amount condition

`LIMIT`, `APPROVAL`, and `TRAVEL_RULE` rules use the same `amount` object:

| Field | Type | Description |
|  --- | --- | --- |
| `threshold` | string | Decimal amount in the asset's units, for example `"10000"` or `"0.5"`. `"0"` is valid. |
| `operator` | string | `GT` (greater than). The schema also lists `GTE`, but Wallet-as-a-Service 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. |


| Kind | Supported `aggregation` values |
|  --- | --- |
| `LIMIT` | `SINGLE_TX`, `ROLLING`, `TOTAL` |
| `APPROVAL` | `SINGLE_TX` |
| `TRAVEL_RULE` | `SINGLE_TX` |


### Example: Rolling limit for counterparty payments

This rule caps payments to any counterparty at 10,000 of the asset in a rolling 24-hour window:

```json
{
  "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` examples, see [Step-up approvals](/products/wallet/user-interface/policies/step-up-approvals#create-a-step-up-approval-rule-with-the-api) and [Set up Travel Rule](/products/wallet/user-interface/travel-rule/travel-rule-setup).

### Response

The API returns the new rule. The rule goes through your organization's **Policy rules** approval flow, so it usually has status `RULE_CREATION_APPROVAL_PENDING` and `active: false`.

```json
{
  "id": "0199a1c2-5d3e-7a10-9b2f-4c8e1d6f0a21",
  "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"
      }
    }
  }
}
```

## Other operations

| Operation | Method and path | Permission |
|  --- | --- | --- |
| List rules | `GET /v2/vaults/{vaultId}/wallets/{walletId}/policy-rules` or `POST …/policy-rules:list` | `policy:read` |
| Get a rule | `GET /v2/vaults/{vaultId}/wallets/{walletId}/policy-rules/{id}` | `policy:read` |
| Delete a rule | `DELETE /v2/vaults/{vaultId}/wallets/{walletId}/policy-rules/{id}` | `policy:delete` |


- **List** supports `pageSize` (default 50, maximum 1,000) and `pageToken`. You can filter by `kind`, `trigger`, `assetId`, `status`, `active`, `createdAt`, and `updatedAt`.
- **No update operation.** To change a rule, create the new rule first, and then delete the old one. This order keeps the wallet protected throughout the change.
- **Wallet scope.** A rule belongs to one wallet. If you request a rule ID under a different wallet, the API returns not found.


The existing `/v2/vaults/{vaultId}/wallets/{walletId}/policy-rules/limits` endpoints keep working, and use the `keylimit:*` permissions. See [Manage policies](/products/wallet/user-interface/policies/policies-manage#create-a-policy-via-the-api).

## Matchers

Matchers use the same types as wallet limits. See [Policy reference](/products/wallet/user-interface/policies/policies-reference#matchers). In the policy rules API:

- Send each matcher as `{ "type": "...", "values": ["..."] }`. Don't use the older single `value` string, which Ripple has deprecated.
- 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.


## Rule statuses

| Status | Enforcing | Description |
|  --- | --- | --- |
| `RULE_CREATION_APPROVAL_PENDING` | No | The rule is waiting for approvers to approve it. |
| `RULE_ENABLED` | Yes | The rule is active. |
| `RULE_REJECTED` | No | Approvers rejected the rule. This status is final. |
| `RULE_DELETION_APPROVAL_PENDING` | Yes | The 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_DELETED` | No | Approvers approved the deletion. This status is final. |
| `RULE_ERROR` | No | Wallet-as-a-Service 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`.

## 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.

| Permission | Lets the credential |
|  --- | --- |
| `policy:create` | Create rules |
| `policy:read` | List and get rules |
| `policy:delete` | Delete rules |


When you [create an API credential](/products/wallet/admin-guide/manage-api-credentials), you can grant each permission at organization, vault, or wallet scope. In the console, owners and administrators can create and delete rules. Approvers, auditors, proposers, and support users can view rules.

## Error codes

| Code | Message | Cause |
|  --- | --- | --- |
| `PAL006.038` | this policy rule type cannot be created yet | Ripple hasn't turned on this rule kind for your organization. |
| `PAL006.039` | an equivalent policy rule already exists | The wallet already has a rule with the same kind, trigger, asset, matchers, and amount. |
| `PAL006.040` | *(varies)* | The rule is invalid. The message names the problem, for example `amount operator GTE is not yet supported`. |
| `PAL006.041` | *(varies)* | The rule doesn't exist on this wallet. |
| `PAL006.046` | travel-rule policy rules require a configured travel-rule connection | You tried to create a `TRAVEL_RULE` rule before you [connected Notabene](/products/wallet/user-interface/travel-rule/travel-rule-setup#connect-your-notabene-account). |
| `PAL006.048` | *(varies)* | You tried to create an `APPROVAL` rule on a wallet that has compliance screening integrations turned on. |


For transaction rejection codes, see [Policy Engine v2: transaction status changes](/products/wallet/changelogs/policy-engine-v2-transaction-status-changes#structured-rejection-details).

## Related documentation

- [Step-up approvals](/products/wallet/user-interface/policies/step-up-approvals)
- [Travel Rule](/products/wallet/user-interface/travel-rule/travel-rule-overview)
- [Policy concepts](/products/wallet/user-interface/policies/policies-concepts)
- [Policy reference](/products/wallet/user-interface/policies/policies-reference)