> ## Documentation Index
> Fetch the complete documentation index at: https://docs.getpara.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Guardrail templates

> Ready-to-use Guardrail policies for spending caps, allowlists, approvals, and message signing on app-owned wallets

Guardrails restrict the app-owned wallets your backend signs with through the Para REST API. A transaction that matches a Guardrail rule is denied or waits for approval. Transactions that no rule matches stay allowed, except raw signatures, which are [denied by default](#allow-solana-message-signing). For user-owned wallets, start from the [Request templates](/v3/general/permissions-request-templates) instead.

You can create the first four templates in the Developer Portal or with the CLI. The last two need the CLI. For the full flows, see the [Developer Portal walkthrough](/v3/general/developer-portal-permissions) and [Author policies with JSON](/v3/cli/permissions). In the CLI commands, replace `KEY_ID` with your beta API key ID and `RECORD_ID` with the `policy.recordId` that `create` returns. The Portal shows the policy it generates in the **JSON Preview** tab.

<Note>
  Amounts are integer strings in base units, not decimal token amounts. ETH uses wei, so 1 ETH is `1000000000000000000`. USDC has 6 decimals, so 1 USDC is `1000000`.
</Note>

## Cap native spending

Deny ETH transfers on Base above 0.1 ETH in one transaction or above 1 ETH in a 24-hour window. Use it to limit how much a compromised or misbehaving backend can move.

<Tabs>
  <Tab title="Developer Portal">
    1. Open **Permissions**, select the **Guardrails** tab, and select **Add**. Once the key has a Guardrail, the button is **New Guardrail**.
    2. Enter a **Guardrail Policy Name**, select **Base (8453)** under **Networks**, and select **Continue**.
    3. Select **Transfer ETH**, then **Add Condition**: **Transaction amount**, **is at most**, `0.1`, **ETH**, **per transaction**.
    4. Select **Add Action**, select **Transfer ETH** again, and add a condition: **Transaction amount**, **is at most**, `1`, **ETH**, **per time window**, `24`, **hours**.
    5. Select **Review**, then **Save Draft**. Activate the draft from the **Drafts** list.
  </Tab>

  <Tab title="CLI">
    ```json guardrail-native-spending-caps.json theme={null}
    {
      "id": "base-native-spending-caps",
      "schemaVersion": "permissions.policy.v1",
      "version": "1",
      "selector": {
        "walletType": "EVM",
        "chainId": "8453"
      },
      "rules": [
        {
          "id": "per-transaction-cap",
          "effect": "deny",
          "actions": ["transfer_native"],
          "when": {
            "kind": "all",
            "of": [
              { "kind": "compare", "fact": "request.classification", "op": "eq", "value": "native_transfer" },
              { "kind": "compare", "fact": "request.value.baseUnits", "op": "gt", "value": "100000000000000000" }
            ]
          }
        },
        {
          "id": "daily-cap",
          "effect": "deny",
          "actions": ["transfer_native"],
          "when": {
            "kind": "all",
            "of": [
              { "kind": "compare", "fact": "request.classification", "op": "eq", "value": "native_transfer" },
              {
                "kind": "aggregate",
                "fact": "wallet.spend.native",
                "op": "gt",
                "value": "1000000000000000000",
                "window": "24h"
              }
            ]
          }
        }
      ],
      "metadata": {
        "productMode": "guardrail",
        "description": "Cap ETH on Base at 0.1 per transaction and 1 per day"
      }
    }
    ```

    ```bash theme={null}
    para keys permissions validate KEY_ID --file guardrail-native-spending-caps.json --environment beta --json
    para keys permissions create KEY_ID --file guardrail-native-spending-caps.json --environment beta --json
    para keys permissions activate RECORD_ID KEY_ID --environment beta --yes --json
    ```
  </Tab>
</Tabs>

The Portal form asks for the amount you permit, and the saved rule denies anything above it. That is why each condition in the JSON uses `gt`.

<Note>
  Spending limits cover native transfers on every wallet type and token transfers on EVM (ERC-20), Solana, Cosmos, and Stellar. Sui limits cover native transfers only. A token limit counts only the asset it names. Each network and asset has its own budget, so a limit of 1 ETH per day on Ethereum and Base allows 1 ETH per day on each network. Amounts Para cannot decode do not count toward a limit. Smart account user operations do not count toward EVM limits, except ERC-20 transfers sent through Para [gas sponsorship](/v3/rest/gas-sponsorship).
</Note>

To cover more EVM networks, select them under **Networks** or set `chainId` to a list such as `["1", "8453"]`. For Solana, Cosmos, Stellar, or Sui, use the **Native transfer** action in the Portal.

## Cap daily USDC transfers

Deny USDC transfers on Base once the wallet would send more than 1,000 USDC in a 24-hour window. USDC on Base is `0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913`.

<Tabs>
  <Tab title="Developer Portal">
    1. Open **Permissions**, select the **Guardrails** tab, and select **Add** or **New Guardrail**.
    2. Enter a **Guardrail Policy Name**, select **Base (8453)** under **Networks**, and select **Continue**.
    3. Select **Transfer tokens** and enter the USDC address in **Contract Address**. **Function Name** is fixed to `transfer`.
    4. Select **Add Condition**: **Transaction amount**, **is at most**, `1000000000` token base units, **per time window**, `24`, **hours**.
    5. Select **Review**, then **Save Draft**. Activate the draft from the **Drafts** list.
  </Tab>

  <Tab title="CLI">
    ```json guardrail-usdc-daily-cap.json theme={null}
    {
      "id": "base-usdc-daily-cap",
      "schemaVersion": "permissions.policy.v1",
      "version": "1",
      "selector": {
        "walletType": "EVM",
        "chainId": "8453"
      },
      "rules": [
        {
          "id": "usdc-daily-cap",
          "effect": "deny",
          "actions": ["transfer_tokens"],
          "when": {
            "kind": "all",
            "of": [
              { "kind": "compare", "fact": "request.classification", "op": "eq", "value": "erc20_transfer" },
              {
                "kind": "any",
                "of": [
                  {
                    "kind": "compare",
                    "fact": "request.contract.address",
                    "op": "ne",
                    "value": "0x833589fcd6edb6e08f4c7c32d4f71b54bda02913"
                  },
                  { "kind": "compare", "fact": "request.method", "op": "ne", "value": "transfer" },
                  {
                    "kind": "aggregate",
                    "fact": "wallet.spend.erc20",
                    "op": "gt",
                    "value": "1000000000",
                    "window": "24h"
                  }
                ]
              }
            ]
          }
        }
      ],
      "metadata": {
        "productMode": "guardrail",
        "description": "Cap USDC transfers on Base at 1,000 per day"
      }
    }
    ```

    ```bash theme={null}
    para keys permissions validate KEY_ID --file guardrail-usdc-daily-cap.json --environment beta --json
    para keys permissions create KEY_ID --file guardrail-usdc-daily-cap.json --environment beta --json
    para keys permissions activate RECORD_ID KEY_ID --environment beta --yes --json
    ```
  </Tab>
</Tabs>

<Warning>
  The contract address is part of the rule, so this Guardrail also denies direct `transfer` calls for every other ERC-20 token on Base. Only USDC transfers within the daily limit pass.
</Warning>

The limit counts direct `transfer(address,uint256)` calls; see [Windowed spend limits](/v3/rest/permissions#windowed-spend-limits) for what it excludes. On Solana, Cosmos, and Stellar, use the **Token transfer** action and enter the asset in **Token Asset**. Sui has no token transfer action.

## Allow only approved recipients

Deny ETH transfers on Base to any address outside an allowlist. Use it when the wallet should only pay addresses you already know.

<Tabs>
  <Tab title="Developer Portal">
    1. Open **Permissions**, select the **Guardrails** tab, and select **Add** or **New Guardrail**.
    2. Enter a **Guardrail Policy Name**, select **Base (8453)** under **Networks**, and select **Continue**.
    3. Select **Transfer ETH**, then **Add Condition**: **Recipient address**, **is one of**, and the approved addresses separated by commas.
    4. Select **Review**, then **Save Draft**. Activate the draft from the **Drafts** list.
  </Tab>

  <Tab title="CLI">
    ```json guardrail-allowed-recipients.json theme={null}
    {
      "id": "base-allowed-recipients",
      "schemaVersion": "permissions.policy.v1",
      "version": "1",
      "selector": {
        "walletType": "EVM",
        "chainId": "8453"
      },
      "rules": [
        {
          "id": "allowed-recipients",
          "effect": "deny",
          "actions": ["transfer_native"],
          "when": {
            "kind": "all",
            "of": [
              { "kind": "compare", "fact": "request.classification", "op": "eq", "value": "native_transfer" },
              {
                "kind": "not",
                "of": {
                  "kind": "compare",
                  "fact": "request.to",
                  "op": "in",
                  "value": [
                    "0x1111111111111111111111111111111111111111",
                    "0x2222222222222222222222222222222222222222"
                  ]
                }
              }
            ]
          }
        }
      ],
      "metadata": {
        "productMode": "guardrail",
        "description": "Send ETH on Base only to approved recipients"
      }
    }
    ```

    ```bash theme={null}
    para keys permissions validate KEY_ID --file guardrail-allowed-recipients.json --environment beta --json
    para keys permissions create KEY_ID --file guardrail-allowed-recipients.json --environment beta --json
    para keys permissions activate RECORD_ID KEY_ID --environment beta --yes --json
    ```
  </Tab>
</Tabs>

This rule covers native ETH transfers only. Token transfers and contract calls to other addresses are not restricted by it. On Solana, Cosmos, Stellar, or Sui, use the **Native transfer** action with the same **Recipient address** condition.

## Allow only an approved contract

Deny contract calls on Base unless they call `deposit` on one approved contract. Use it when the wallet should interact with a single protocol function.

<Tabs>
  <Tab title="Developer Portal">
    1. Open **Permissions**, select the **Guardrails** tab, and select **Add** or **New Guardrail**.
    2. Enter a **Guardrail Policy Name**, select **Base (8453)** under **Networks**, and select **Continue**.
    3. Select **Call a contract**. Enter the contract in **Contract Address** and `deposit` in **Function Name**. No condition is needed.
    4. Select **Review**, then **Save Draft**. Activate the draft from the **Drafts** list.
  </Tab>

  <Tab title="CLI">
    ```json guardrail-allowed-contracts.json theme={null}
    {
      "id": "base-allowed-contract",
      "schemaVersion": "permissions.policy.v1",
      "version": "1",
      "selector": {
        "walletType": "EVM",
        "chainId": "8453"
      },
      "rules": [
        {
          "id": "allowed-contract-call",
          "effect": "deny",
          "actions": ["call_contract"],
          "when": {
            "kind": "all",
            "of": [
              { "kind": "compare", "fact": "request.classification", "op": "eq", "value": "decoded_contract_call" },
              {
                "kind": "any",
                "of": [
                  {
                    "kind": "compare",
                    "fact": "request.contract.address",
                    "op": "ne",
                    "value": "0x3333333333333333333333333333333333333333"
                  },
                  { "kind": "compare", "fact": "request.method", "op": "ne", "value": "deposit" }
                ]
              }
            ]
          }
        }
      ],
      "metadata": {
        "productMode": "guardrail",
        "description": "Call only deposit on the approved Base contract"
      }
    }
    ```

    ```bash theme={null}
    para keys permissions validate KEY_ID --file guardrail-allowed-contracts.json --environment beta --json
    para keys permissions create KEY_ID --file guardrail-allowed-contracts.json --environment beta --json
    para keys permissions activate RECORD_ID KEY_ID --environment beta --yes --json
    ```
  </Tab>
</Tabs>

<Warning>
  This rule matches only contract calls that Para can decode. A call whose data Para cannot decode does not match and stays allowed. Adding a second **Call a contract** action does not extend the allowlist either: each action denies every call outside its own contract and function.

  To deny every contract call outside a list of contracts, including calls Para cannot decode, author the rule in JSON with this `when`:

  ```json theme={null}
  {
    "kind": "not",
    "of": {
      "kind": "compare",
      "fact": "request.target",
      "op": "in",
      "value": ["0x3333333333333333333333333333333333333333", "0x4444444444444444444444444444444444444444"]
    }
  }
  ```
</Warning>

Contract calls here are the `call_contract` action. ERC-20 transfers and approvals, Safe transactions, and deployments are separate actions. On Solana, Cosmos, Stellar, or Sui, use the **Execution target** action in the Portal.

## Require approval above a threshold

Hold ETH transfers on Base above 1 ETH until one person with the `approver` role approves them. Smaller transfers sign without review. The Developer Portal forms cannot express approval requirements, so create this policy with the CLI.

<Info>
  Para enables conditional approvals per app. Until then, validating this policy returns a `permissions_v2_extension_unavailable` error, and the approval administration endpoints return `403 PERMISSIONS_V2_EXTENSION_UNAVAILABLE`.
</Info>

```json guardrail-approval-threshold.json theme={null}
{
  "id": "base-large-transfer-approval",
  "schemaVersion": "permissions.policy.v1",
  "version": "1",
  "selector": {
    "walletType": "EVM",
    "chainId": "8453"
  },
  "rules": [
    {
      "id": "large-transfer-approval",
      "effect": "require_approval",
      "actions": ["transfer_native"],
      "when": { "kind": "compare", "fact": "request.value.baseUnits", "op": "gt", "value": "1000000000000000000" },
      "approvalRequirement": {
        "id": "large-transfer-review",
        "evidence": "authenticated_approval",
        "stages": [
          { "id": "approvers", "requiredApprovals": 1, "eligibility": [{ "kind": "role", "name": "approver" }] }
        ]
      }
    }
  ],
  "metadata": {
    "productMode": "guardrail",
    "description": "Require one approver for ETH transfers above 1 ETH on Base"
  }
}
```

```bash theme={null}
para keys permissions validate KEY_ID --file guardrail-approval-threshold.json --environment beta --json
para keys permissions create KEY_ID --file guardrail-approval-threshold.json --environment beta --json
para keys permissions activate RECORD_ID KEY_ID --environment beta --yes --json
```

Before testing, [assign the `approver` role](/v3/rest/permissions-administration#assign-roles-and-attributes) to at least one user and [bind the wallet to the authorization scope](/v3/rest/permissions-administration#bind-a-wallet-to-the-scope). A transfer above the threshold returns `403 POLICY_REVIEW_REQUIRED` with an `approvalCaseId`. Retry it after approval as described in [Conditional approvals](/v3/rest/permissions#conditional-approvals).

To require more people or several stages, extend `stages` as shown in [Conditional approval requirements](/v3/references/permissions/policy-json#conditional-approval-requirements). Guardrails accept only `authenticated_approval` evidence.

## Allow Solana message signing

Allow `sign-message` and `sign-raw` requests from app-owned Solana wallets.

While Permissions is on, Para denies raw signatures for app-owned and user-owned wallets unless an active policy that applies to the wallet has a rule allowing the `raw_signature` action. Raw signatures include `sign-raw` requests and message signing from wallets that cannot sign as EVM, such as Solana, Stellar, and Sui wallets. The Developer Portal forms cannot create this rule; use [JSON authoring](/v3/cli/permissions).

```json guardrail-solana-message-signing.json theme={null}
{
  "id": "solana-allow-message-signing",
  "schemaVersion": "permissions.policy.v1",
  "version": "1",
  "selector": {
    "walletType": "SOLANA"
  },
  "rules": [
    {
      "id": "allow-raw-signatures",
      "effect": "allow",
      "actions": ["raw_signature"],
      "when": { "kind": "compare", "fact": "para.wallet.hasUserOwner", "op": "eq", "value": false }
    }
  ],
  "metadata": {
    "productMode": "guardrail",
    "description": "Allow message signing from app-owned Solana wallets"
  }
}
```

```bash theme={null}
para keys permissions validate KEY_ID --file guardrail-solana-message-signing.json --environment beta --json
para keys permissions create KEY_ID --file guardrail-solana-message-signing.json --environment beta --json
para keys permissions activate RECORD_ID KEY_ID --environment beta --yes --json
```

A `raw_signature` rule can read only `para.wallet.*` facts. The condition `para.wallet.hasUserOwner` equals `false` holds for every app-owned wallet, and a Guardrail applies only to app-owned wallets. As a result, the rule allows raw signatures from every Solana wallet the selector covers. Add `walletId` to the selector to limit it to specific wallets.

To allow raw signatures for another wallet type, set `walletType` to `COSMOS`, `STELLAR`, `SUI`, or `EVM`, which covers `sign-raw` requests. A policy governs one wallet type, so write one policy per wallet type.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.