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

# Facts and conditions

> Condition kinds, operators, value encodings, limits and every fact a Permissions policy can read, by wallet type

export const Card = ({imgUrl, compactImgUrl, title, description, href, horizontal = false, newTab = false}) => {
  const [isHovered, setIsHovered] = useState(false);
  const handleClick = e => {
    e.preventDefault();
    if (newTab) {
      window.open(href, '_blank', 'noopener,noreferrer');
    } else {
      window.location.href = href;
    }
  };
  return <div className={`not-prose relative my-2 p-[1px] rounded-xl transition-all duration-300 ${isHovered && href ? 'bg-gradient-to-r from-[#FF4E00] to-[#874AE3]' : 'bg-gray-200'}`} onMouseEnter={() => setIsHovered(true)} onMouseLeave={() => setIsHovered(false)}>
      <a href={href} onClick={href ? handleClick : undefined} className={`not-prose flex ${horizontal ? 'flex-row' : 'flex-col'} font-normal h-full bg-white overflow-hidden w-full ${href ? 'cursor-pointer' : 'cursor-default'} rounded-[11px] no-underline`}>
        {imgUrl && <div className={`relative overflow-hidden flex-shrink-0 ${horizontal ? 'w-[30%] rounded-l-[11px]' : 'w-full'}`} onClick={e => e.stopPropagation()}>
            <picture style={{
    display: 'contents'
  }}>
              {compactImgUrl && <source media="(max-width: 639px)" srcSet={compactImgUrl} />}
              <img src={imgUrl} alt={title} className="w-full h-full object-cover pointer-events-none select-none" draggable="false" />
            </picture>
            <div className="absolute inset-0 pointer-events-none" />
          </div>}
        <div className={`flex-grow px-6 py-5 ${horizontal ? 'w-[70%]' : 'w-full'} flex flex-col ${horizontal && imgUrl ? 'justify-center' : 'justify-start'}`}>
          {title && <h2 className="font-semibold text-base text-gray-800 m-0">{title}</h2>}
          {description && <div className={`font-normal text-gray-500 re leading-6 ${horizontal || !imgUrl ? 'mt-0' : 'mt-1'}`}>
              <p className="m-0 text-xs">{description}</p>
            </div>}
        </div>
      </a>
    </div>;
};

A rule's condition tests facts: values Para reads from the signing request, from the wallet's record, or from the spend it tracks. This page lists every fact a policy can read for each wallet type, with its value type, operators, actions and value format. [Policy JSON schema](/v3/references/permissions/policy-json) covers the document around the conditions.

<Tip>
  The CLI prints the same catalog for your API key: run `para keys permissions catalog --json`.
</Tip>

<Info>
  Validate conditions against a beta API key, shown as **Development** in the Developer Portal. Policies reach production only through a production review: select **Request production review** on your production key.
</Info>

## Condition kinds

Leaf conditions read one fact. Group conditions combine other conditions, so one rule can express AND, OR and NOT logic.

<Tabs>
  <Tab title="compare">
    ```json theme={null}
    { "kind": "compare", "fact": "request.to", "op": "in", "value": ["0x1111111111111111111111111111111111111111"] }
    ```

    | Field | Type | Required | Description |
    | - | - | - | - |
    | `kind` | string | Yes | `"compare"` |
    | `fact` | string | Yes | A fact key from the tables on this page, other than a [metered fact](#metered-facts). |
    | `op` | string | Yes | One of the fact's operators. |
    | `value` | JSON value | Yes | A value of the fact's type. See [Values and encodings](#values-and-encodings). |
    | `parameter` | object | No | Lets each user change the value. See [User-adjustable parameters](#user-adjustable-parameters). |
  </Tab>

  <Tab title="aggregate">
    ```json theme={null}
    { "kind": "aggregate", "fact": "wallet.spend.native", "op": "lte", "value": "1000000000000000000", "window": "24h" }
    ```

    | Field | Type | Required | Description |
    | - | - | - | - |
    | `kind` | string | Yes | `"aggregate"` |
    | `fact` | string | Yes | A [metered fact](#metered-facts). |
    | `op` | string | Yes | `lt`, `lte`, `gt` or `gte`. |
    | `value` | string | Yes | The limit in base units. |
    | `window` | string | Yes | The window length, such as `"24h"`. See [Windows](#windows). |
    | `asset` | string | No | The token the limit counts. Required for `wallet.spend.token` on Solana, Cosmos and Stellar. |
    | `parameter` | object | No | Lets each user change the limit. See [User-adjustable parameters](#user-adjustable-parameters). |
  </Tab>

  <Tab title="all, any, not">
    ```json theme={null}
    {
      "kind": "all",
      "of": [
        { "kind": "compare", "fact": "request.value.baseUnits", "op": "gt", "value": "0" },
        { "kind": "not", "of": { "kind": "compare", "fact": "para.wallet.isPregen", "op": "eq", "value": true } }
      ]
    }
    ```

    | Kind | `of` | Matches when |
    | - | - | - |
    | `all` | An array of at least one condition | Every condition matches. |
    | `any` | An array of at least one condition | At least one condition matches. |
    | `not` | One condition object, not an array | The condition does not match. |
  </Tab>
</Tabs>

A leaf can also be indeterminate, when the fact is missing from the request, or not applicable, when a metered fact does not apply. [Condition outcomes](/v3/references/permissions/policy-json#condition-outcomes) shows how groups combine them.

## Operators

Each fact supports the operators its value type allows. The fact tables list them per fact.

| Operator | Matches when | `value` |
| - | - | - |
| `eq` | The fact equals the value. | One value of the fact's type |
| `ne` | The fact does not equal the value. | One value of the fact's type |
| `lt`, `lte`, `gt`, `gte` | The fact is less than, at most, greater than, or at least the value. | An amount string |
| `in` | The fact equals one of the values. | An array of values of the fact's type |
| `in` on `request.actionFamilies` | The request contains the family. | One family name as a string, not an array |

| Value type | `compare` operators | `aggregate` operators |
| - | - | - |
| string | `eq`, `ne`, `in` | |
| address | `eq`, `ne`, `in` | |
| amount | `eq`, `ne`, `lt`, `lte`, `gt`, `gte` | `lt`, `lte`, `gt`, `gte`, metered facts only |
| boolean | `eq`, `ne` | |
| string\_array | `in` | |

<Note>
  `ne` does not match a missing fact. A rule that reads a fact the request does not provide is indeterminate, whatever the operator.
</Note>

## Values and encodings

Each value must have the fact's type. A wrong type fails validation with `value_type_mismatch`.

| Type | JSON form | Comparison |
| - | - | - |
| amount | A string of digits in the asset's smallest unit, such as `"1000000"` for 1 USDC. JSON numbers are rejected. | `eq` and `ne` compare strings exactly, so `"1"` and `"01"` differ. Ordering operators compare integers without a size limit. Use digit strings without leading zeros. |
| address | `0x` followed by 40 hex characters | Case-insensitive |
| string | A JSON string | Exact and case-sensitive, except values shaped like an EVM address, which compare case-insensitively. EVM token facts have a [contract-address exception](#identifiers-by-wallet-type). |
| boolean | `true` or `false`, not a string | Exact |
| string\_array | Used only by `request.actionFamilies` | `in` with one string |

Amounts are base units: wei and token units on EVM (1 USDC is `"1000000"` with 6 decimals), lamports on Solana, the chain's base denom on Cosmos (such as `uatom`), stroops on Stellar, and MIST on Sui. Token amounts use the token's smallest unit as well: the mint's decimals on Solana, the denom's integer amount on Cosmos, and 7 decimals for every Stellar asset, so 1 USDC on Stellar is `"10000000"`.

### Identifiers by wallet type

Write addresses and assets in the canonical form of the policy's wallet type, exactly as shown.

<Warning>
  Validation checks `request.to`, `request.target` and `request.token.asset` on Solana, Cosmos and Stellar, and `request.to` and `request.target` on Sui, and rejects other forms with `chain_identity_invalid`. It does not check identity formats for `request.value.asset` on any wallet type or for EVM `request.to`, `request.target`, `request.token.asset`, `request.safe.to`, `request.safe.value.asset` and `request.safe.token.asset`. Safe amount facts still require digit strings and reject other values with `value_type_mismatch`.

  A malformed identity string can validate and still match a `ne` condition against a present valid value.
</Warning>

| Wallet type | `request.to` | `request.target` | `request.value.asset` | `request.token.asset` |
| - | - | - | - | - |
| EVM | `0x` address | `0x` address | `eip155:<chainId>:native` | `eip155:<chainId>:token:<contract>`, with the contract address in lowercase |
| Solana | Base58 public key | Base58 program ID | `solana:native` | `solana:token:<mint>` |
| Cosmos | Bech32 address. When every chain the selector names is one Para knows, the prefix must match one of them, such as `cosmos` for `cosmoshub-4`. | Bech32 contract address | The chain's native denom, such as `uatom` | `cosmos:<chainId>:<denom>` for a denom other than the native one. When the selector names chains, `<chainId>` must be one of them. IBC denoms are `ibc/` followed by 64 uppercase hex characters. |
| Stellar | `G...` account or `M...` muxed account | `C...` contract | `stellar:testnet:native`, `stellar:mainnet:native` or `stellar:custom:<hash>:native` | `stellar:<network>:<code>:<issuer>`, with a network of `testnet`, `mainnet` or `custom:<hash>`. When the selector names networks, it must be one of them. |
| Sui | `0x` followed by 64 lowercase hex characters | Package ID in the same form | `sui:native` | Not available |

For a Stellar network other than testnet or mainnet, `<hash>` is the SHA-256 hash of the network passphrase in lowercase hex. On EVM, `eq`, `ne` and `in` on `request.token.asset` and `request.safe.token.asset` compare the contract-address part of `eip155:<chainId>:token:<contract>` without regard to case. The prefix and chain ID must still match exactly.

## Limits

Condition groups and leaves share these depth and count limits; metered facts also constrain window declarations.

| Limit | Value | Error |
| - | - | - |
| Condition depth, counting the leaf | 6 levels | `max_condition_depth_exceeded` |
| Conditions per rule, counting groups and leaves | 12 | `max_condition_count_exceeded` |
| Children of `all` and `any` | At least 1 | `partner_policy_schema_invalid` |
| Window lengths per metered fact | One per policy | `metered_fact_window_conflict` |

## Reading the fact tables

The tables below list every fact a policy can read for each wallet type, outside [Metered facts](#metered-facts).

The Actions column lists the rule actions that can read the fact. Every action in a rule's `actions` must appear there, or validation fails with `fact_action_unsupported`. `all` means every action of the wallet type, including `raw_signature`, and `-` means no rule can read the fact.

The Present column says whether every request of those actions carries the fact. "May be missing" means Para provides the fact only when it can read it from the request, such as a recipient decoded from calldata. A rule that reads a missing fact is indeterminate and can deny the request; see [Facts Para cannot read](/v3/references/permissions/policy-json#facts-para-cannot-read).

`"actions": "all"` covers every action of the wallet type, `raw_signature` included, but the validation fact check leaves `raw_signature` out. An `"all"` rule can therefore read only `request.classification`, the `para.wallet.*` facts and, on Solana, Cosmos, Stellar and Sui, `request.actionFamilies`. A raw signature has no `request.actionFamilies`, so a condition on it is indeterminate on a raw signature unless another condition in its group settles it, and Para then denies the raw signature unless another matching rule has the same or a stronger effect. See [Facts Para cannot read](/v3/references/permissions/policy-json#facts-para-cannot-read).

A rule that names `raw_signature` can read only the `para.wallet.*` facts.

## EVM

Choose facts by the signing action: EVM transaction and message actions expose different fields.

| Fact | Type | Operators | Actions | Allowed values | Present |
| - | - | - | - | - | - |
| `request.classification` | string | `eq` `ne` `in` | `transfer_native` `transfer_tokens` `approve_tokens` `transfer_nfts` `call_contract` `deploy_contract` `execute_safe_transaction` `sign_message` `sign_typed_data` `sign_authorization` | See [Classification values](#classification-values) | Always |
| `request.method` | string | `eq` `ne` `in` | `transfer_tokens` `approve_tokens` `transfer_nfts` `call_contract` `execute_safe_transaction` `sign_typed_data` | | Always on `sign_typed_data`. May be missing on other actions. |
| `request.args[n]` | string | `eq` `ne` `in` | `transfer_tokens` `approve_tokens` `transfer_nfts` `call_contract` `execute_safe_transaction` | | May be missing |
| `request.to` | string | `eq` `ne` `in` | `transfer_native` `transfer_tokens` `transfer_nfts` `call_contract` | | Always on `transfer_native`. May be missing on other actions. |
| `request.target` | string | `eq` `ne` `in` | `transfer_native` `transfer_tokens` `approve_tokens` `transfer_nfts` `call_contract` `execute_safe_transaction` | | Always |
| `request.contract.address` | address | `eq` `ne` `in` | `transfer_tokens` `approve_tokens` `transfer_nfts` `call_contract` `execute_safe_transaction` | | May be missing |
| `request.token.asset` | string | `eq` `ne` `in` | `transfer_tokens` `approve_tokens` | | May be missing |
| `request.token.value.baseUnits` | amount | `eq` `ne` `lt` `lte` `gt` `gte` | `transfer_tokens` `approve_tokens` | | May be missing |
| `request.value.baseUnits` | amount | `eq` `ne` `lt` `lte` `gt` `gte` | `transfer_native` `transfer_tokens` `approve_tokens` `transfer_nfts` `call_contract` `deploy_contract` `execute_safe_transaction` | | Always |
| `request.value.asset` | string | `eq` `ne` `in` | `transfer_native` `transfer_tokens` `approve_tokens` `transfer_nfts` `call_contract` `deploy_contract` `execute_safe_transaction` | | Always |
| `request.safe.to` | string | `eq` `ne` `in` | `execute_safe_transaction` `sign_typed_data` | | May be missing. See [Safe payments](#safe-payments). |
| `request.safe.value.baseUnits` | amount | `eq` `ne` `lt` `lte` `gt` `gte` | `execute_safe_transaction` `sign_typed_data` | | May be missing. See [Safe payments](#safe-payments). |
| `request.safe.value.asset` | string | `eq` `ne` `in` | `execute_safe_transaction` `sign_typed_data` | | May be missing. See [Safe payments](#safe-payments). |
| `request.safe.token.value.baseUnits` | amount | `eq` `ne` `lt` `lte` `gt` `gte` | `execute_safe_transaction` `sign_typed_data` | | May be missing. See [Safe payments](#safe-payments). |
| `request.safe.token.asset` | string | `eq` `ne` `in` | `execute_safe_transaction` `sign_typed_data` | | May be missing. See [Safe payments](#safe-payments). |
| `request.gas.limit` | amount | `eq` `ne` `lt` `lte` `gt` `gte` | `transfer_native` `transfer_tokens` `approve_tokens` `transfer_nfts` `call_contract` `deploy_contract` `execute_safe_transaction` | | Always on `deploy_contract`. May be missing on other actions. |
| `request.gas.maxFeePerGas` | amount | `eq` `ne` `lt` `lte` `gt` `gte` | `transfer_native` `transfer_tokens` `approve_tokens` `transfer_nfts` `call_contract` `deploy_contract` `execute_safe_transaction` | | May be missing |
| `request.gas.maxPriorityFeePerGas` | amount | `eq` `ne` `lt` `lte` `gt` `gte` | `transfer_native` `transfer_tokens` `approve_tokens` `transfer_nfts` `call_contract` `deploy_contract` `execute_safe_transaction` | | May be missing |
| `request.gas.gasPrice` | amount | `eq` `ne` `lt` `lte` `gt` `gte` | `transfer_native` `transfer_tokens` `approve_tokens` `transfer_nfts` `call_contract` `deploy_contract` `execute_safe_transaction` | | May be missing |
| `request.typedData.primaryType` | string | `eq` `ne` `in` | `sign_typed_data` | | Always |
| `request.approvalLike` | boolean | `eq` `ne` | `transfer_native` `transfer_tokens` `approve_tokens` `transfer_nfts` `sign_typed_data` `sign_authorization` | | Always on `transfer_native`, `sign_typed_data` and `sign_authorization`. May be missing on other actions. |
| `request.raw.opaque` | boolean | `eq` `ne` | - | | Not readable |
| `para.wallet.isPregen` | boolean | `eq` `ne` | all | | Always |
| `para.wallet.hasUserOwner` | boolean | `eq` `ne` | all | | Always |
| `para.wallet.crossPartnerUsed` | boolean | `eq` `ne` | all | | Always |

What each fact holds on EVM:

| Fact | Value |
| - | - |
| `request.classification` | The kind of request. See [Classification values](#classification-values). |
| `request.method` | The decoded function name, such as `transfer`. On typed data, the EIP-712 primary type. |
| `request.args[n]` | Decoded argument `n` of the call, from 0, as a string. Integers are decimal strings. For `transfer`, `args[0]` is the recipient and `args[1]` the amount. |
| `request.to` | The recipient: the transaction's `to` for a native transfer, or the decoded recipient of an ERC-20 or NFT transfer. On a decoded contract call, the contract address. Approvals have none. |
| `request.target` | The transaction's `to` address, such as the token contract of an ERC-20 transfer. For an ERC-20 transfer sent through Para gas sponsorship, it is the token contract. Use it to allowlist contracts, because it is present even when Para cannot decode the call. |
| `request.contract.address` | The contract of a decoded call. |
| `request.token.asset` | The token of a decoded ERC-20 transfer or approval, as `eip155:<chainId>:token:<contract>`. |
| `request.token.value.baseUnits` | The declared transfer or approval amount, not the executed balance change. |
| `request.value.baseUnits` | The native value the transaction sends, including value attached to a contract call. It is not the total cost of the call. |
| `request.value.asset` | `eip155:<chainId>:native` |
| `request.safe.to` | The recipient of a readable Safe payment: the inner call's `to` for a native payment, or the decoded recipient of the ERC-20 transfer. |
| `request.safe.value.baseUnits` | The native amount a readable Safe payment sends. On `execute_safe_transaction`, `request.value.baseUnits` stays the value the signing wallet attaches to the outer `execTransaction` call. |
| `request.safe.value.asset` | `eip155:<chainId>:native` |
| `request.safe.token.value.baseUnits` | The ERC-20 amount a readable Safe payment transfers. |
| `request.safe.token.asset` | The token of a readable Safe payment, as `eip155:<chainId>:token:<contract>` with the contract address in lowercase. |
| `request.gas.*` | The gas limit and fee fields the transaction sets. `maxFeePerGas` and `maxPriorityFeePerGas` appear on EIP-1559 transactions, and `gasPrice` on legacy transactions. |
| `request.typedData.primaryType` | The EIP-712 primary type, such as `Permit`. |
| `request.approvalLike` | `true` for token approvals, EIP-712 messages whose primary type contains "permit", and EIP-7702 authorizations. |
| `para.wallet.isPregen` | `true` for a pregenerated wallet. |
| `para.wallet.hasUserOwner` | `true` when a user owns the wallet. |
| `para.wallet.crossPartnerUsed` | `true` when the wallet has been used with more than one Para app. |

The Para client and server SDKs, including their viem, ethers and wagmi integrations, sign EIP-712 typed data and EIP-7702 authorizations as a message over their hash, so Para evaluates those requests as `sign_message`. The `sign_typed_data` and `sign_authorization` actions, and the facts Para reads from typed data and authorizations, such as `request.typedData.primaryType`, apply only to the REST API's `sign-typed-data` and `sign-authorization` routes, which the REST SDK calls.

Para decodes ERC-20 and NFT calls itself. For other contract calls it uses the contract's verified ABI from a block explorer. Calls it cannot decode have the classification `undecodable_contract_call` and no `request.method`, `request.args[n]`, `request.to` or `request.contract.address`.

A native send that carries any calldata is `call_contract`, not `transfer_native`. `request.value.baseUnits` is present on every EVM transaction, `"0"` when it attaches no value, so a native amount cap can name both actions.

<Note>
  `request.raw.opaque` is in the catalog, but no rule can read it. Para sets it only on raw signatures, and `raw_signature` rules read only `para.wallet.*` facts, so validation rejects it with `fact_action_unsupported` on every action.
</Note>

Spending limits use the [metered facts](#metered-facts).

### Safe payments

The `request.safe.*` facts describe the payment inside a Safe transaction. Para reads them from two kinds of request:

* An `execTransaction` call, with the action `execute_safe_transaction`.
* A `SafeTx` EIP-712 message sent to REST `sign-typed-data`, with the action `sign_typed_data`. Its domain must hold only `chainId` and `verifyingContract`, its message must hold exactly the `SafeTx` fields, and its `types` must declare only `SafeTx`, optionally with an `EIP712Domain` of those two fields; otherwise Para treats it as opaque. A `SafeTx` signed through the Para client and server SDKs arrives as `sign_message` and has no Safe facts.

<Warning>
  Safe facts and Safe spend limits apply only to these two requests. A `SafeTx` that an owner wallet co-signs through the SDKs is a `sign_message`, and an owner wallet's direct call to the Safe, such as `approveHash`, is a `call_contract`, so no rule on Safe facts sees either. Once the Safe has enough approvals, anyone can execute the payment outside Para. To block direct calls, see [Direct calls to the Safe](#direct-calls-to-the-safe).
</Warning>

Para reads a Safe transaction as a payment only when all of these hold:

* `operation` is `0`, a Call. A DelegateCall is never a payment.
* `gasPrice` is `0`, so the Safe pays no gas refund.
* The inner `to` is not the Safe itself.
* The inner call is a plain native transfer, or one ERC-20 `transfer` or `transferFrom` that sends no native value.

A readable payment provides `request.safe.to` and the two facts of its payment family. The facts of the other family are not applicable.

| Payment | Provides | Not applicable |
| - | - | - |
| Native | `request.safe.value.baseUnits`, `request.safe.value.asset` | `request.safe.token.value.baseUnits`, `request.safe.token.asset` |
| ERC-20 | `request.safe.token.value.baseUnits`, `request.safe.token.asset` | `request.safe.value.baseUnits`, `request.safe.value.asset` |

A condition on a fact of the other family is not applicable and never matches. A rule that reads only native payment facts therefore does not apply to an ERC-20 payment, or the reverse, and the request goes on to your other rules or the default.

<Warning>
  In a rule that reads both families, Para treats the absent family as missing instead. Its conditions on the absent family are indeterminate, so the rule is indeterminate unless another condition settles the group, and an indeterminate rule can deny the payment with `missing-fact`. For example, a `deny` rule that matches `any` native amount above 1 ETH or token amount above 1,000 USDC denies a payment of 0.1 ETH. Write a separate rule for each family.
</Warning>

A zero-amount payment is still readable. Its amount conditions are evaluated as usual, so `"op": "gt", "value": "0"` does not match it, and the other family stays not applicable.

Every other Safe transaction is opaque, and all of its `request.safe.*` facts are missing. Opaque transactions include:

* DelegateCalls and MultiSend batches.
* `SafeTx` typed data in another shape, such as a domain with extra fields.
* Transactions with a nonzero `gasPrice`.
* Calls to the Safe itself, such as owner and threshold changes.
* Nested Safe calls, such as `execTransaction` or `approveHash` on another Safe.
* Contract calls other than one ERC-20 `transfer` or `transferFrom`, and calldata Para cannot decode.
* An ERC-20 transfer that also sends native value.

An owner wallet's direct call to the Safe is not a Safe transaction and has no Safe facts. See [Direct calls to the Safe](#direct-calls-to-the-safe).

A rule that reads a `request.safe.*` fact is indeterminate on an opaque transaction and can deny it with `missing-fact`. An approval does not help: once another rule's approval case completes, the approved rule no longer counts, so the indeterminate rule can still deny the signing request.

Three facts are the same for a readable payment and an opaque transaction: `request.classification`, `request.method` and `request.typedData.primaryType`. On `execute_safe_transaction`, the decoded `execTransaction` arguments separate DelegateCalls and gas refunds: `request.args[3]` is the `operation` and `request.args[6]` is the `gasPrice`, and both are `"0"` in every readable payment. No fact separates the other opaque forms, or any opaque `SafeTx` typed data.

<Warning>
  Typed data other than a `SafeTx`, such as an ERC-2612 `Permit`, has no Safe facts either. A `sign_typed_data` rule that reads them is indeterminate on that typed data whatever its effect, so Para denies the request with `missing-fact` unless another matching rule has the same or a stronger effect. This holds for `require_approval` and `allow` rules too, so the request is denied instead of going to review.
</Warning>

To keep a `sign_typed_data` rule that reads Safe facts from denying other typed data, add a `request.typedData.primaryType` test to an `all` group:

```json theme={null}
{
  "id": "deny-unlisted-safetx-recipients",
  "effect": "deny",
  "actions": ["sign_typed_data"],
  "when": {
    "kind": "all",
    "of": [
      { "kind": "compare", "fact": "request.typedData.primaryType", "op": "eq", "value": "SafeTx" },
      {
        "kind": "not",
        "of": { "kind": "compare", "fact": "request.safe.to", "op": "in", "value": ["0x1111111111111111111111111111111111111111"] }
      }
    ]
  }
}
```

`request.safe.to` names the inner recipient whatever native value the outer call carries. On `execute_safe_transaction`, constrain `request.value.baseUnits` as well. `sign_typed_data` has no `request.value.*` facts, so a rule that reads them cannot name both actions.

This Guardrail covers app-owned wallets that execute Safe transactions on Base. The first rule denies Safe payments to anyone except two recipients, and because it reads `request.safe.to`, it also denies every opaque `execTransaction`. It does not cover direct calls to the Safe; see [Direct calls to the Safe](#direct-calls-to-the-safe).

The second rule denies a USDC payment that would take the Safe past 50,000 USDC in the current day. It counts only readable payments, so on its own it would not stop a MultiSend batch, a DelegateCall or a gas refund, and the first rule denies those. A payment that passes both rules falls to the Guardrail default, and the wallet signs it.

```json safe-payouts.json theme={null}
{
  "id": "safe-payouts",
  "schemaVersion": "permissions.policy.v1",
  "version": "1",
  "selector": { "walletType": "EVM", "chainId": "8453" },
  "metadata": {
    "productMode": "guardrail",
    "label": "Safe payouts",
    "description": "Safe payments go only to listed recipients, with up to 50,000 USDC a day"
  },
  "rules": [
    {
      "id": "deny-unlisted-safe-recipients",
      "effect": "deny",
      "actions": ["execute_safe_transaction"],
      "when": {
        "kind": "not",
        "of": {
          "kind": "compare",
          "fact": "request.safe.to",
          "op": "in",
          "value": ["0x1111111111111111111111111111111111111111", "0x2222222222222222222222222222222222222222"]
        }
      }
    },
    {
      "id": "deny-safe-usdc-over-daily-limit",
      "effect": "deny",
      "actions": ["execute_safe_transaction"],
      "when": {
        "kind": "aggregate",
        "fact": "safe.spend.erc20",
        "op": "gt",
        "value": "50000000000",
        "window": "24h",
        "asset": "0x833589fcd6edb6e08f4c7c32d4f71b54bda02913"
      }
    }
  ]
}
```

### Direct calls to the Safe

An owner wallet that calls the Safe directly, such as `approveHash(safeTxHash)` to approve a payment on chain, sends a `call_contract` with no Safe facts, so Safe rules and Safe spend limits never see it. A rule that reads Safe facts cannot name `call_contract`; validation fails with `fact_action_unsupported`. To block these calls, deny every `call_contract` whose `request.target` is the Safe:

```json theme={null}
{
  "id": "deny-direct-calls-to-safe",
  "effect": "deny",
  "actions": ["call_contract"],
  "when": { "kind": "compare", "fact": "request.target", "op": "in", "value": ["0x3333333333333333333333333333333333333333"] }
}
```

Calls to other contracts and token transfers still sign, and `execTransaction` stays under your Safe rules. Use `require_approval` instead of `deny` to hold these calls for review. `request.target` is always present and compared without case, so the rule works whether or not Para decodes the call. Do not test `request.method` instead: it is missing when Para cannot decode the call, so that rule would deny every contract call Para cannot decode.

## Solana

Solana's catalog separates native and token transfer facts from program targets.

| Fact | Type | Operators | Actions | Allowed values | Present |
| - | - | - | - | - | - |
| `request.classification` | string | `eq` `ne` `in` | `transfer_native` `transfer_tokens` `call_contract` | See [Classification values](#classification-values) | Always |
| `request.actionFamilies` | string\_array | `in` | `transfer_native` `transfer_tokens` `call_contract` | `native_transfer` `token_transfer` `contract_call` `other` | Always |
| `request.to` | string | `eq` `ne` `in` | `transfer_native` `transfer_tokens` | | May be missing |
| `request.target` | string | `eq` `ne` `in` | `transfer_native` `transfer_tokens` `call_contract` | | May be missing |
| `request.token.asset` | string | `eq` `ne` `in` | `transfer_tokens` | | May be missing |
| `request.token.value.baseUnits` | amount | `eq` `ne` `lt` `lte` `gt` `gte` | `transfer_tokens` | | May be missing |
| `request.value.baseUnits` | amount | `eq` `ne` `lt` `lte` `gt` `gte` | `transfer_native` | | May be missing |
| `request.value.asset` | string | `eq` `ne` `in` | `transfer_native` | | May be missing |
| `para.wallet.isPregen` | boolean | `eq` `ne` | all | | Always |
| `para.wallet.hasUserOwner` | boolean | `eq` `ne` | all | | Always |
| `para.wallet.crossPartnerUsed` | boolean | `eq` `ne` | all | | Always |

On Solana, Cosmos, Stellar and Sui, `request.actionFamilies` lists the kinds of action a transaction contains. `request.to` and the amount facts are present only when the transaction contains exactly one action; Cosmos retains `request.target` when `MsgExecuteContract` is the transaction's only message, even if attached funds add transfer actions. Solana compute budget instructions do not count as an action. Creating the recipient's associated token account does not count either when the transaction's only other instruction is a `TransferChecked` that follows it and sends that token to the new account. Every other instruction counts, such as a Memo or wrapping SOL, so a rule that reads a recipient or amount can deny a transfer that includes one.

* `request.to` is the recipient of a SOL transfer. For a token transfer it is the destination token account. A wallet address in your condition also matches that wallet's associated token account for the transfer's mint, for SPL Token and Token-2022. Another token account the wallet owns does not match.
* `request.target` is the program the instruction calls.
* `request.token.asset` names the mint, as `solana:token:<mint>`. It does not identify the cluster, so select the network in the policy's `chainId`.
* Para reads a token transfer's recipient, amount and mint only from a `TransferChecked` instruction. A plain `Transfer` or `TransferCheckedWithFee` has none of these facts, and token spending limits do not count it.

```json solana-usdc-payouts.json theme={null}
{
  "id": "solana-usdc-payouts",
  "schemaVersion": "permissions.policy.v1",
  "version": "1",
  "selector": { "walletType": "SOLANA", "chainId": "solana:mainnet" },
  "metadata": {
    "productMode": "delegation",
    "label": "USDC payouts",
    "description": "Send up to 100 USDC a day to our payout account"
  },
  "rules": [
    {
      "id": "allow-usdc-payouts",
      "effect": "allow",
      "actions": ["transfer_tokens"],
      "when": {
        "kind": "all",
        "of": [
          { "kind": "compare", "fact": "request.token.asset", "op": "eq", "value": "solana:token:EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v" },
          { "kind": "compare", "fact": "request.to", "op": "eq", "value": "9WzDXwBbmkg8ZTbNMqUxvQRAyrZzDsGYdLVL9zYtAWWM" },
          {
            "kind": "aggregate",
            "fact": "wallet.spend.token",
            "op": "lte",
            "value": "100000000",
            "window": "24h",
            "asset": "solana:token:EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v"
          }
        ]
      }
    }
  ]
}
```

## Cosmos

Cosmos facts distinguish bank sends from contract calls. The notes below describe which requests provide recipient, target and amount fields.

| Fact | Type | Operators | Actions | Allowed values | Present |
| - | - | - | - | - | - |
| `request.classification` | string | `eq` `ne` `in` | `transfer_native` `transfer_tokens` `call_contract` | See [Classification values](#classification-values) | Always |
| `request.actionFamilies` | string\_array | `in` | `transfer_native` `transfer_tokens` `call_contract` | `native_transfer` `token_transfer` `contract_call` `other` | Always |
| `request.to` | string | `eq` `ne` `in` | `transfer_native` `transfer_tokens` | | May be missing |
| `request.target` | string | `eq` `ne` `in` | `transfer_native` `transfer_tokens` `call_contract` | | May be missing |
| `request.token.asset` | string | `eq` `ne` `in` | `transfer_tokens` | | May be missing |
| `request.token.value.baseUnits` | amount | `eq` `ne` `lt` `lte` `gt` `gte` | `transfer_tokens` | | May be missing |
| `request.value.baseUnits` | amount | `eq` `ne` `lt` `lte` `gt` `gte` | `transfer_native` | | May be missing |
| `request.value.asset` | string | `eq` `ne` `in` | `transfer_native` | | May be missing |
| `para.wallet.isPregen` | boolean | `eq` `ne` | all | | Always |
| `para.wallet.hasUserOwner` | boolean | `eq` `ne` | all | | Always |
| `para.wallet.crossPartnerUsed` | boolean | `eq` `ne` | all | | Always |

* `request.to` is the recipient of a bank send.
* `request.target` is the contract a single `MsgExecuteContract` calls. Transfers have none.
* A bank send is a native transfer only in the chain's native denom, on `cosmoshub-4`, `noble-1`, `osmosis-1`, `celestia`, `dydx-mainnet-1` and `injective-1`. Any other denom, or any bank send on another chain, is a token transfer.
* As on Solana, recipients, targets and amounts are present only for a transaction with exactly one message.

## Stellar

Stellar facts cover payments and contract calls; the notes below explain operation-specific availability.

| Fact | Type | Operators | Actions | Allowed values | Present |
| - | - | - | - | - | - |
| `request.classification` | string | `eq` `ne` `in` | `transfer_native` `transfer_tokens` `call_contract` | See [Classification values](#classification-values) | Always |
| `request.actionFamilies` | string\_array | `in` | `transfer_native` `transfer_tokens` `call_contract` | `native_transfer` `token_transfer` `contract_call` `other` | Always |
| `request.to` | string | `eq` `ne` `in` | `transfer_native` `transfer_tokens` | | May be missing |
| `request.target` | string | `eq` `ne` `in` | `call_contract` | | May be missing |
| `request.token.asset` | string | `eq` `ne` `in` | `transfer_tokens` | | May be missing |
| `request.token.value.baseUnits` | amount | `eq` `ne` `lt` `lte` `gt` `gte` | `transfer_tokens` | | May be missing |
| `request.value.baseUnits` | amount | `eq` `ne` `lt` `lte` `gt` `gte` | `transfer_native` | | May be missing |
| `request.value.asset` | string | `eq` `ne` `in` | `transfer_native` | | May be missing |
| `para.wallet.isPregen` | boolean | `eq` `ne` | all | | Always |
| `para.wallet.hasUserOwner` | boolean | `eq` `ne` | all | | Always |
| `para.wallet.crossPartnerUsed` | boolean | `eq` `ne` | all | | Always |

* `request.to` is the destination account of a payment.
* `request.target` is the contract a Soroban `invokeContract` call targets.
* `request.token.asset` and `request.value.asset` include the network, so a condition written for `stellar:testnet` does not match on mainnet.
* Recipients, targets and amounts are present only for a transaction with exactly one operation.
* `createAccount` is a `transfer_native`. A path payment carries `transfer_native` when any of its assets is XLM and `transfer_tokens` when any is another asset, with no recipient or amount facts, so a rule that reads an amount is indeterminate on it and spending limits do not count it.

## Sui

Sui exposes native transfer and Move call facts. The notes below describe transfer families and the limits of token authoring.

| Fact | Type | Operators | Actions | Allowed values | Present |
| - | - | - | - | - | - |
| `request.classification` | string | `eq` `ne` `in` | `transfer_native` `call_contract` | See [Classification values](#classification-values) | Always |
| `request.actionFamilies` | string\_array | `in` | `transfer_native` `call_contract` | `native_transfer` `token_transfer` `contract_call` `other` | Always |
| `request.to` | string | `eq` `ne` `in` | `transfer_native` | | May be missing |
| `request.target` | string | `eq` `ne` `in` | `call_contract` | | May be missing |
| `request.value.baseUnits` | amount | `eq` `ne` `lt` `lte` `gt` `gte` | `transfer_native` | | May be missing |
| `request.value.asset` | string | `eq` `ne` `in` | `transfer_native` | | May be missing |
| `para.wallet.isPregen` | boolean | `eq` `ne` | all | | Always |
| `para.wallet.hasUserOwner` | boolean | `eq` `ne` | all | | Always |
| `para.wallet.crossPartnerUsed` | boolean | `eq` `ne` | all | | Always |

* `request.to` is the recipient of a SUI transfer.
* `request.target` is the package a Move call targets.
* Sui has no `transfer_tokens` action, authorable token facts or token spending limits. Only a rule that names `"all"` covers Sui token transfers. With trusted coin evidence, a readable `SplitCoins` followed by `TransferObjects` can report `token_transfer` in `request.actionFamilies`; unrecognized transfers report `other`.
* Recipients, targets and amounts are present only for a transaction with exactly one action, such as a single transfer.

## Classification values

`request.classification` accepts these 22 values on every wallet type. Values that a wallet type never produces never match, and a value outside this list fails validation with `value_not_allowed_for_fact`.

| Value | Produced for | Action |
| - | - | - |
| `native_transfer` | A native transfer on EVM, or a transaction holding a single native transfer on Solana, Cosmos, Stellar or Sui | `transfer_native` |
| `erc20_transfer` | An ERC-20 `transfer` call | `transfer_tokens` |
| `erc20_transfer_from` | An ERC-20 `transferFrom` call | `transfer_tokens` |
| `erc20_approval` | An ERC-20 `approve` call | `approve_tokens` |
| `nft_transfer` | An NFT `safeTransferFrom` call | `transfer_nfts` |
| `decoded_contract_call` | Another EVM contract call that Para decoded | `call_contract` |
| `undecodable_contract_call` | An EVM contract call that Para could not decode | `call_contract` |
| `deploy_contract` | An EVM contract deployment | `deploy_contract` |
| `safe_exec_transaction` | A Safe `execTransaction` call | `execute_safe_transaction` |
| `message_signing` | An EVM personal message, including typed data and authorizations signed through the Para SDKs | `sign_message` |
| `evm.typed_data` | EIP-712 typed data sent to REST `sign-typed-data` | `sign_typed_data` |
| `evm.authorization` | An EIP-7702 authorization sent to REST `sign-authorization` | `sign_authorization` |
| `smart_account_user_operation` | An ERC-20 transfer sent through Para gas sponsorship | `transfer_tokens` |
| `solana.transaction` | Any other Solana transaction | By its actions |
| `cosmos.transaction` | Any other Cosmos transaction | By its actions |
| `stellar.xdr` | Any other Stellar transaction | By its actions |
| `sui.transaction` | Any other Sui transaction | By its actions |
| `evm.raw_signature` | An EVM `sign-raw` request | `raw_signature` |
| `raw.opaque_message` | A `sign-raw` request from a Solana, Cosmos, Stellar or Sui wallet; a message from a wallet that cannot sign as EVM; or a REST `sign-message` request from a Cosmos wallet without `walletType: "EVM"`. Messages from an EVM-capable Cosmos wallet use `message_signing` through the SDKs. | `raw_signature` |
| `unsupported` | A request Para does not support | None. Para denies it before rules run. |
| `evm.batch_mixed` | Not currently produced | |
| `cosmos.transaction_message` | Not currently produced | |

Only a rule with `"actions": "all"` can read the classification of a raw signature, `evm.raw_signature` or `raw.opaque_message`. A rule that names `raw_signature` reads only `para.wallet.*` facts.

## Metered facts

Metered facts read the spend Para tracks for a wallet over a time window. Use them only in `aggregate` conditions; a `compare` condition on a metered fact fails with `metered_fact_window_required`.

| Fact | Type | Operators | Actions | Wallet types | Asset |
| - | - | - | - | - | - |
| `wallet.spend.native` | amount | `lt` `lte` `gt` `gte` | `transfer_native` `transfer_tokens` `approve_tokens` `transfer_nfts` `call_contract` `deploy_contract` `execute_safe_transaction` | EVM, Solana, Cosmos, Stellar, Sui | Not allowed |
| `wallet.spend.erc20` | amount | `lt` `lte` `gt` `gte` | `transfer_tokens` | EVM | Optional `0x` token contract address |
| `wallet.spend.token` | amount | `lt` `lte` `gt` `gte` | `transfer_tokens` | EVM, Solana, Cosmos, Stellar | Optional `0x` token contract address on EVM. Required on Solana, Cosmos and Stellar, in the `request.token.asset` form. |
| `safe.spend.native` | amount | `lt` `lte` `gt` `gte` | `execute_safe_transaction` `sign_typed_data` | EVM | Not allowed |
| `safe.spend.erc20` | amount | `lt` `lte` `gt` `gte` | `execute_safe_transaction` `sign_typed_data` | EVM | Optional `0x` token contract address |

<Note>
  The Actions column covers all wallet types. On EVM, `wallet.spend.native` is available to all seven listed actions, because any EVM transaction can send native value. On Solana, Cosmos, Stellar and Sui it is available only to `transfer_native`. `wallet.spend.token` does not apply to Sui, which has no token transfer action. `safe.spend.native` and `safe.spend.erc20` count only Safe payments; see [Safe spend](#safe-spend).
</Note>

### Windows

`window` is a positive integer followed by a unit: `ms`, `m`, `h` or `d`, such as `"30m"`, `"24h"` or `"7d"`. There is no week or month unit. The Developer Portal form writes windows in hours, `"24h"` for a day and `"168h"` for a week, and the examples on these pages follow it.

Windows are fixed buckets aligned to 1970-01-01 00:00 UTC, not rolling periods. A `24h` window resets at 00:00 UTC every day, and a `168h` window resets every Thursday at 00:00 UTC.

A policy can use only one window length for each metered fact. `"24h"` and `"1d"` count as the same length, but `"24h"` and `"168h"` on the same fact fail with `metered_fact_window_conflict`. To enforce both a daily and a weekly limit, put them in separate policies.

### What the limit compares

Para compares the spend already recorded in the current window, plus spend reserved by requests still in progress, plus the current request's amount, against `value`. A rule with `"op": "lte", "value": "1000000"` therefore matches while the total after this request stays at or below 1,000,000.

### What counts

Metering counts the transfer forms below, which can be narrower than a rule's action coverage.

| Wallet type | `wallet.spend.native` | `wallet.spend.erc20` and `wallet.spend.token` |
| - | - | - |
| EVM | A transaction's positive native value, on any action. Messages and smart account user operations do not count. | Direct ERC-20 `transfer` calls and ERC-20 transfers sent through Para gas sponsorship. `transferFrom`, approvals, and transfers made inside other contracts do not count. |
| Solana | A positive SOL transfer on a network Para can identify | A `TransferChecked` of the named token. A plain `Transfer` or `TransferCheckedWithFee` does not count. |
| Cosmos | A positive bank send in the native denom, on the chains listed in [Cosmos](#cosmos) | A bank send of the named denom |
| Stellar | A positive native `payment` or `createAccount` | A `payment` of the named asset. Path payments, `accountMerge` and `createClaimableBalance` do not count, for XLM or tokens. |
| Sui | A positive native transfer on a network Para can identify | Not available |

<Warning>
  On EVM, a token spending limit therefore does not cap `transferFrom` calls or approvals. A per-transaction rule on `request.token.value.baseUnits` covers `transferFrom`, and a rule that names `approve_tokens` can limit approvals.

  A spending limit alone does not deny the Solana and Stellar transfers it does not count. To stop them, add a `deny` rule on the same action that reads the amount, such as `request.token.value.baseUnits`. It is indeterminate on those transfers, so Para denies them.
</Warning>

On `execute_safe_transaction`, `wallet.spend.native` counts only the native value the signing wallet attaches to the outer call. The payment the Safe makes counts toward [Safe spend](#safe-spend) instead.

Gas and network fees never count. On Solana and Sui, metering needs a network proven from the signed transaction; if configured proof endpoints prove no network, Para denies the request with `network-unverifiable`. On Cosmos, native spend counts only on the chains listed in [Cosmos](#cosmos).

When a request has nothing to count for a condition, such as a zero-value transaction or a transfer of another token, the condition is not applicable and the rule does not match through it. Readable Solana native transfers and `TransferChecked` token transfers are summed by asset for metering, even when the transaction has no single recipient or amount fact. A meter whose applicable amount cannot be resolved is indeterminate.

### Budgets

Each budget is kept per wallet, network, asset and window length.

* A token limit with `asset` counts only that token. An EVM token limit without `asset` keeps a separate budget for each token.
* `wallet.spend.erc20` and `wallet.spend.token` share the same budget on EVM.
* Budgets are shared across your policies and rules. Two rules that meter the same wallet, network, asset and window read the same spend.
* A selector with several networks does not pool them. A daily limit on Ethereum and Base allows that amount on each network.
* Safe spend budgets are kept per Safe instead of per wallet. See [Safe spend](#safe-spend).

### When spend is recorded

Para reserves the request's amount before signing and records the spend when the signing operation succeeds. Para can release the reservation when it knows signing did not proceed. If a dispatched call fails while the signer outcome is uncertain, the amount can remain reserved.

A request adds to a budget only when a rule that meters that budget covers it: the rule that decided the request, a matching `allow` rule, or a `deny` or `require_approval` rule whose other conditions match the request. A payment that falls outside a limit rule's other conditions, such as a transfer to a recipient the rule does not name, does not count toward that limit.

Spend is reserved only for requests Para allows. A request approved through `require_approval` can take a budget past its limit. So can a request that went past an `allow` rule's limit and was then approved, for example by the user in the review pop-up. It still counts toward that limit when the rule's other conditions match it. If another request uses up the budget between the decision and the reservation, Para denies the request. The denial reason is `windowed-spend-limit-exceeded`.

### Safe spend

`safe.spend.native` and `safe.spend.erc20` count readable [Safe payments](#safe-payments), from `execTransaction` calls and from `SafeTx` messages sent to REST `sign-typed-data`. Wallet limits do not count these payments.

* Each budget is kept per API key, Safe, network, asset and window length. Your Guardrails and Requests share it: every `safe.spend` rule with that window length reads the same spend, whichever wallet signs.
* Owners who sign the same Safe transaction hash share one count. An `execTransaction` can use a counted co-signature's credit when the payment matches, that co-signature has not already covered another execution, and Para recovers its signer from the first signature slot. A request covered by an already-counted key or this execution credit adds nothing, so the limit does not block it.
* `safe.spend.erc20` without `asset` counts every ERC-20 payment, with a separate budget for each token.
* A zero-amount native payment does not count, and neither does an ERC-20 `transferFrom` that pulls tokens from an address other than the Safe. The condition is not applicable to them.

<Note>
  Execution credit supports ECDSA signatures with `v` of `27` or `28`, in hex buffers with at most 32 complete 65-byte slots. Only the first complete slot is inspected. A matching signature in a later slot, another signature mode or failed signer recovery receives no credit, so the execution counts separately unless its own request count key was already counted.
</Note>

<Warning>
  A Safe spend limit counts only payments Para can read. Its conditions are not applicable to MultiSend batches, DelegateCalls, calls that pay a gas refund and every other [opaque Safe transaction](#safe-payments), so when no other rule decides one, [Para's default for the wallet](/v3/references/permissions/policy-json#when-no-rule-matches) applies and an app-owned wallet signs it. Pair the limit with a rule that reads a `request.safe.*` fact, such as the first rule of `safe-payouts.json`, or add a `request.safe.*` condition to the limit rule, so that Para denies opaque transactions with `missing-fact`. Neither rule sees an owner wallet's direct calls to the Safe; see [Direct calls to the Safe](#direct-calls-to-the-safe).
</Warning>

<Warning>
  Para does not check that a wallet owns the Safe. A payment counts toward the budget of the Safe it names (the `execTransaction` target or the `SafeTx` domain's `verifyingContract`) whenever a `safe.spend` rule [covers the request](#when-spend-is-recorded), so a wallet that is not an owner can use up the Safe's budget. Scope each policy that uses `safe.spend` to the Safe's owner wallets: list them with `walletId` in a Guardrail, or list the users who own them with `userId` in a Request.
</Warning>

### Reading usage

Read current spend with `getUserPolicyUsage` in the client SDKs or `getPartnerPolicyUsage` in the REST SDK. Each meter reports the window, its reset time, and the committed, reserved and used amounts. Meters for `allow` rules that use `lt` or `lte` outside `not` and `any` also report the remaining amount. Each limit condition is its own meter, so when one rule has two limits on the same fact and window, the rule allows only the smaller `remainingBaseUnits`. See [Permissions types](/v3/references/types/permissions) for the response shape.

Safe meters appear only when the usage request passes `safeAddress`. In the client SDKs, they also need one of the user's wallets to have made a payment request that Para metered against that Safe on that network.

## User-adjustable parameters

A Request can let each user set their own amount for a limit. Add a `parameter` to a `compare` or `aggregate` condition, and its `value` becomes the default.

```json theme={null}
{
  "kind": "aggregate",
  "fact": "wallet.spend.native",
  "op": "lte",
  "value": "100000000000000000",
  "window": "24h",
  "parameter": { "id": "daily-limit", "label": "Daily limit" }
}
```

| Field | Type | Required | Description |
| - | - | - | - |
| `id` | string | Yes | Unique within the policy. Users' saved values refer to it. |
| `label` | string | Yes | The name users see next to the input. |

| Rule | Detail | Error |
| - | - | - |
| Facts | `compare` on `request.value.baseUnits` or `request.token.value.baseUnits`, or `aggregate` on a metered fact | `parameter_not_allowed` |
| Operators | `lt` or `lte` in `allow` rules. `gt` or `gte` in `deny` and `require_approval` rules. | `parameter_not_allowed` |
| Placement | Anywhere except inside `not` | `parameter_not_allowed` |
| Default | A non-negative integer string without leading zeros | `parameter_value_invalid` |
| Identity | Each `id` once per policy | `parameter_id_duplicate` |

* Only Request policies can be personalized. A Guardrail with a parameter validates, but its default always applies.
* A user who changes their values must consent to the policy again.
* Para enables adjustable parameters per API key, so beta and production keys are enabled separately. Until it does, validating or saving a policy with a parameter fails with `permissions_v2_extension_unavailable`.

<Warning>
  Para does not bound the values users choose. A user can set a higher limit than the default, so if you need a ceiling, add a second condition without a `parameter` to the same `all` group. A Guardrail cannot cap it, because Guardrails never govern user-owned wallets.
</Warning>

<Warning>
  After you publish a new version of the policy, each user's saved values need their confirmation. Until the user confirms, Para denies that user's requests that the policy selects, with the reason `parameter_configuration_invalid`.
</Warning>

<CardGroup cols={2}>
  <Card imgUrl="/images/v3/cards/concept-policy-json.png" title="Policy JSON schema" description="Selectors, rules, effects and how Para combines policies." href="/v3/references/permissions/policy-json" />

  <Card imgUrl="/images/v3/cards/concept-approval-requirements.png" title="Approval requirements" description="Stages, eligibility and amount bands for require_approval rules." href="/v3/references/permissions/approval-requirements" />
</CardGroup>


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